tc39 / proposal-type-annotations

ECMAScript proposal for type syntax that is erased - Stage 1
https://tc39.es/proposal-type-annotations/
4.27k stars 47 forks source link

TypeScript Alternative: TypeSafe #198

Closed Malfunctionify closed 1 year ago

Malfunctionify commented 1 year ago

Proposal: Native Alternative to TypeScript

Problem Statement

In our project, we're exploring alternatives to TypeScript to address specific requirements. We aim to leverage a native alternative that offers several compelling features:

/*
Native Alternative to TypeScript Features:
* Built-in type-check with errors on mismatched types.
* Fast performance!
* Warns when too many variables are supplied!
* Supports any known JavaScript type!
* Provides clear error messages with original error line numbers!
* Detects multiple type errors!
*/

Proposed Solution

Our proposal is to investigate and potentially adopt this native alternative to TypeScript to enhance our development workflow and meet our project's unique needs.

Plan

  1. Research and evaluate the native alternative to TypeScript to understand its compatibility with our project requirements.
  2. Conduct performance testing to assess its speed and efficiency compared to TypeScript.
  3. Explore its type-checking capabilities and error reporting to ensure they align with our development goals.
  4. Determine if this native alternative can effectively address our specific use cases and provide a seamless development experience.

Expected Outcomes

By considering this native alternative to TypeScript, we aim to improve our development process by leveraging its unique features and capabilities. This proposal will help us make an informed decision about its adoption based on thorough research and testing.

Proposed Solution

Native Alternative to TypeScript

We propose adopting a "Native Alternative to TypeScript" for our project. This alternative offers a set of unique features and capabilities that align with our specific requirements and development goals:

Output

Running the Native Alternative

To run the native alternative and experience its benefits, follow these steps:

  1. Begin by creating a file named types.js, as demonstrated in the example provided below.

  2. Launch your terminal.

  3. Proceed to the directory within your project where the native alternative code is situated.

  4. Initiate the code execution with Node.js using the following command:

node types.js

Example Output

After running the code with Node.js, you can expect to see output similar to the following:

------------WARMING UP STRESS PERFORMANCE TEST-----------------
Warm Up Stress Test (1000 iterations):
5 seconds...
4 seconds...
3 seconds...
2 seconds...
1 seconds...

Warm Up Stress Tests are starting now.

WarmUpExampleSimpleFunction: 3.788ms
WarmUpExampleTypeFunctionNoCache: 20.88ms
WarmUpExampleTypeFunctionNoCacheV2: 20.605ms
WarmUpExampleTypeFunctionWithCache: 19.68ms
WarmUpExampleTypeFunctionWithCacheV2: 26.276ms
WarmUpExampleTypeV3: 1.47ms
------------STRESS PERFORMANCE TEST-----------------
Stress Test (1000 iterations):
5 seconds...
4 seconds...
3 seconds...
2 seconds...
1 seconds...

Stress Tests are starting now.

exampleSimpleFunction: 0.193ms
exampleTypeFunctionNoCache: 23.596ms
exampleTypeFunctionNoCacheV2: 19.503ms
exampleTypeFunctionWithCache: 21.523ms
exampleTypeFunctionWithCacheV2: 22.138ms
exampleTypeV3: 0.328ms
------------WARMING UP STRESS PERFORMANCE TEST-----------------
Warm Up Stress Test (1000 iterations):
5 seconds...
4 seconds...
3 seconds...
2 seconds...
1 seconds...

Warm Up Stress Tests are starting now.

WarmUpExampleSimpleFunction: 0.439ms
WarmUpExampleTypeFunctionNoCache: 20.299ms
WarmUpExampleTypeFunctionNoCacheV2: 20.522ms
WarmUpExampleTypeFunctionWithCache: 20.39ms
WarmUpExampleTypeFunctionWithCacheV2: 22.301ms
WarmUpExampleTypeV3: 0.317ms
-------------------------------------------------------- Finished:
- Average execution time for Simplified function (no type checking): 0.00008000046014785766ms - AVG Total: 0.08000046014785767s - Proof: 42000
- Average execution time for Original "type" function (no cache): 0.023442699253559112ms - AVG Total: 23.442699253559113s - Proof: 42000
- Average execution time for Original "typeV2" function (no cache): 0.019141399085521697ms - AVG Total: 19.141399085521698s - Proof: 42000
- Average execution time for Modified "type" function (with cache): 0.021363500118255614ms - AVG Total: 21.363500118255615s - Proof: 42000
- Average execution time for Modified "typeV2" function (with cache): 0.021949101388454437ms - AVG Total: 21.949101388454437s - Proof: 42000
- Average execution time for Modified "typeV3" function: 0.0002245003581047058ms - AVG Total: 0.2245003581047058s - Proof: 42000
---------------
The fastest method, except Simplified, is: 'type' function V3 with an execution time of 0.0002245003581047058 milliseconds

All tests completed.
Press any key to continue . . .

This output showcases the native alternative's functionality and provides insights into its performance analysis, allowing us to make informed decisions regarding its adoption.

types.js

console.clear(); // Just put it in the devtools to start the demo
/*
Native Alternative to TypeScript Features:
* Built-in type-check with errors on mimatch types.
* Fast performance!
* Warn when too many variables are supplied!
* Supports any known javascript type!
* See original error line numbers!
* Multiple type errors detection!
*/

// ----------------------------------------------------- Example Type Functions: (Choose the best or make a better one!)

// Original 'type' function with no caching
function type(func, types) {
  return (...args) => {
    const lineNumber = new Error().stack.split('\n')[2].match(/:(\d+):\d+/)[1];
    const errors = Object.entries(types).reduce((acc, [argName, expectedType]) => (
      typeof args[0][argName] !== expectedType ? acc.concat(`- Argument '${argName}' must be of type ${expectedType}, but instead, it was provided as ${typeof args[0][argName]}.`) : acc
    ), []);
    const warnings = Object.keys(args[0]).filter(argName => !types.hasOwnProperty(argName)).map(argName => `- Argument '${argName}' has no type definition`);
    if (warnings.length > 0) console.warn(`Warning: Argument without type definition (Line ${lineNumber})\n${warnings.join('\n')}`);
    if (errors.length > 0) throw new TypeError(`Error: Type Mismatch (Line ${lineNumber})\n${errors.join('\n')}`);
    return func(...args);
  };
}

// Original 'typeV2' function with no caching and with actualType
function typeV2(func, types) {
  return (...args) => {
    const lineNumber = new Error().stack.split('\n')[2].match(/:(\d+):\d+/)[1];
    const errors = Object.entries(types).reduce((acc, [argName, expectedType]) => {
      const actualType = typeof args[0][argName];
      return actualType !== expectedType ? acc.concat(`- Argument '${argName}' must be of type ${expectedType}, but instead, it was provided as ${actualType}.`) : acc
    }, []);
    const warnings = Object.keys(args[0]).filter(argName => !types.hasOwnProperty(argName)).map(argName => `- Argument '${argName}' has no type definition`);
    if (warnings.length > 0) console.warn(`Warning: Argument without type definition (Line ${lineNumber})\n${warnings.join('\n')}`);
    if (errors.length > 0) throw new TypeError(`Error: Type Mismatch (Line ${lineNumber})\n${errors.join('\n')}`);
    return func(...args);
  };
}

// 'type' function with caching
function typeWithCache(func, initialTypes) {
  const typeCache = new WeakMap();
  return (...args) => {
    const lineNumber = new Error().stack.split('\n')[2].match(/:(\d+):\d+/)[1];
    let cachedTypes = typeCache.get(func);
    if (!cachedTypes) {
      cachedTypes = initialTypes;
      typeCache.set(func, cachedTypes);
    }
    const errors = Object.entries(cachedTypes).reduce((acc, [argName, expectedType]) => (
      typeof args[0][argName] !== expectedType ? acc.concat(`- Argument '${argName}' must be of type ${expectedType}, but instead, it was provided as ${typeof args[0][argName]}.`) : acc
    ), []);
    const warnings = Object.keys(args[0]).filter(argName => !cachedTypes.hasOwnProperty(argName)).map(argName => `- Argument '${argName}' has no type definition`);
    if (warnings.length > 0) console.warn(`Warning: Argument without type definition (Line ${lineNumber})\n${warnings.join('\n')}`);
    if (errors.length > 0) throw new TypeError(`Error: Type Mismatch (Line ${lineNumber})\n${errors.join('\n')}`);
    return func(...args);
  };
}

// 'typeV2' function with caching and with actualType
function typeV2WithCache(func, initialTypes) {
  const typeCache = new WeakMap();
  return (...args) => {
    const lineNumber = new Error().stack.split('\n')[2].match(/:(\d+):\d+/)[1];
    let cachedTypes = typeCache.get(func);
    if (!cachedTypes) {
      cachedTypes = initialTypes;
      typeCache.set(func, cachedTypes);
    }
    const errors = Object.entries(cachedTypes).reduce((acc, [argName, expectedType]) => {
      const actualType = typeof args[0][argName];
      return actualType !== expectedType ? acc.concat(`- Argument '${argName}' must be of type ${expectedType}, but instead, it was provided as ${actualType}.`) : acc
    }, []);
    const warnings = Object.keys(args[0]).filter(argName => !cachedTypes.hasOwnProperty(argName)).map(argName => `- Argument '${argName}' has no type definition`);
    if (warnings.length > 0) console.warn(`Warning: Argument without type definition (Line ${lineNumber})\n${warnings.join('\n')}`);
    if (errors.length > 0) throw new TypeError(`Error: Type Mismatch (Line ${lineNumber})\n${errors.join('\n')}`);
    return func(...args);
  };
}

// V3
function typeV3(func, types) {
  const typeEntries = Object.entries(types);
  const typeCount = typeEntries.length;

  return (...args) => {
    const argObj = args[0];
    const errors = [];
    const warnings = [];
    for (let i = 0; i < typeCount; i++) {
      const [argName, expectedType] = typeEntries[i];
      const actualType = typeof argObj[argName];
      if (actualType !== expectedType) errors.push(`- Argument '${argName}' must be of type ${expectedType}, but instead, it was provided as ${actualType}.`);
    }
    const argNames = Object.keys(argObj);
    const argNameCount = argNames.length;
    for (let i = 0; i < argNameCount; i++) {
      const argName = argNames[i];
      if (!types.hasOwnProperty(argName)) warnings.push(`- Argument '${argName}' has no type definition`);
    }
    if (warnings.length > 0 || errors.length > 0) {
      const lineNumber = new Error().stack.split('\n')[2].match(/:(\d+):\d+/)[1];
      if (warnings.length > 0) console.warn(`Warning: Argument without type definition (Line ${lineNumber})\n${warnings.join('\n')}`);
      if (errors.length > 0) throw new TypeError(`Error: Type Mismatch (Line ${lineNumber})\n${errors.join('\n')}`);
    }
    return func(...args);
  };
}

// ----------------------------------------------------- Example Usage:

const exampleSimpleFunction = function (args) {
  // Your custom logic here
  return args.a + args.b;
};

// Example Usage
const addition = typeV3(function (args) { // Used for the Introduction
  // Your custom logic here
  // args is an object containing named arguments
  // You can access and use the arguments as needed
  // console.log(args);
  return args.a + args.b; // Simple addition
}, { a: 'number', b: 'number', c: 'string' }); // If you want to define this on the top, just flip the type function variables here and there

/*
  // Optionally, You can unpack your variables instead of calling them under args:
  const { a, b, c } = args;
  console.log(a, b, c);
*/
/*
  // If you want a real JavaScript proposal, Please tell them to add this:
  const { ... } = args;
  console.log(a, b, c);
*/
/*
  // You can loop through the arguments but this is a really bad way of using the args:
  for (const argName in args) {
    if (Object.hasOwnProperty.call(args, argName)) {
      const argValue = args[argName];
      console.log(`${argName}: ${argValue}`);
    }
  }
*/

const exampleTypeFunctionNoCache = type(function (args) {
  // Your custom logic here
  return args.a + args.b;
}, { a: 'number', b: 'number', c: 'string' });

const exampleTypeFunctionNoCacheV2 = typeV2(function (args) {
  // Your custom logic here
  return args.a + args.b;
}, { a: 'number', b: 'number', c: 'string' });

const exampleTypeFunctionWithCache = typeWithCache(function (args) {
  // Your custom logic here
  return args.a + args.b;
}, { a: 'number', b: 'number', c: 'string' });

const exampleTypeFunctionWithCacheV2 = typeV2WithCache(function (args) {
  // Your custom logic here
  return args.a + args.b;
}, { a: 'number', b: 'number', c: 'string' });

const exampleTypeV3 = typeV3(function (args) {
  // Your custom logic here
  return args.a + args.b;
}, { a: 'number', b: 'number', c: 'string' });

// ----------------------------------------------------- Let The Tests Begin:
async function runTests() {
  const stressTestIterations = 1000; // Number of iterations for averaging
  // Sleep for 5 seconds before starting the tests
  console.log("Counting down to start the tests:");
  for (let i = 5; i > 0; i--) {
    console.log(`${i} seconds...`);
    await sleep(1000);
  }
  console.log("\nTests are starting now.\n");
  await sleep(1000); // Sleep for an extra second before running the tests

  // ----------------------------------------------------- Simple Examples: (Introduction)
  console.clear(); // Clears the console
  console.log("-----------SIMPLE EXAMPLES------------------")

  await sleep(1000); // Sleep for an extra second before running the tests

  console.log("Simple Type-Safe Addition (40+2):")
  const result = addition({ a: 40, b: 2, c: 'Hello' }); // Works fine
  console.log("result:", result); // 42

  await sleep(5000); // Sleep for 5 seconds before the next test

  console.log("\nExample Type-Safe Errors:")
  await sleep(1000); // Sleep for an extra second before running the tests
  try {
    const output = addition({ a: '5', b: '10', c: 'Hello' }); // Throws a TypeError for non-number arguments
    console.log("output:", output); // Never runs
  } catch (e) {
    console.error(e.message); // Customized error message without stack trace
  }

  await sleep(3000); // Sleep for 3 seconds before the next test

  try {
    const output = addition({ a: 5, b: 10, c: 20 }); // Throws a TypeError for c not being a string
    console.log("output:", output); // Never runs
  } catch (e) {
    console.error(e.message); // Customized error message without stack trace
  }

  await sleep(3000); // Sleep for 3 seconds before the next test

  console.log('\nExample For Too Many Variables (40+2):');
  await sleep(1000); // Sleep for an extra second before running the tests
  try {
    const output = addition({ a: 40, b: 2, c: 'hey', test: 'I run' }); // Throws a TypeError for c not being a string
    console.log("output:", output); // Never runs
  } catch (e) {
    console.error(e.message); // Customized error message without stack trace
  }

  await sleep(10000); // Sleep for 10 seconds before the next test
  console.clear(); // Clears the console
  // ----------------------------------------------------- WarmU[ ]Stress Peformance Test:
  console.log("------------WARMING UP STRESS PERFORMANCE TEST-----------------")
  await sleep(1000); // Sleep for an extra second before running the tests

  console.log(`Warm Up Stress Test (${stressTestIterations} iterations):`);
  for (let i = 5; i > 0; i--) {
    console.log(`${i} seconds...`);
    await sleep(1000);
  }
  console.log("\nWarm Up Stress Tests are starting now.\n");
  await sleep(1000); // Sleep for an extra second before running the tests

  // Measure execution time for Simplified function (no type checking)
  console.time('WarmUpExampleSimpleFunction');
  measureExecutionTime(exampleSimpleFunction, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleSimpleFunction');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Original "type" function (no cache)
  console.time('WarmUpExampleTypeFunctionNoCache');
  measureExecutionTime(exampleTypeFunctionNoCache, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionNoCache');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Original "typeV2" function (no cache)
  console.time('WarmUpExampleTypeFunctionNoCacheV2');
  measureExecutionTime(exampleTypeFunctionNoCacheV2, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionNoCacheV2');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "type" function
  console.time('WarmUpExampleTypeFunctionWithCache');
  measureExecutionTime(exampleTypeFunctionWithCache, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionWithCache');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "typeV2" function
  console.time('WarmUpExampleTypeFunctionWithCacheV2');
  measureExecutionTime(exampleTypeFunctionWithCacheV2, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionWithCacheV2');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "typeV3" function
  console.time('WarmUpExampleTypeV3');
  measureExecutionTime(exampleTypeV3, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeV3');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // ----------------------------------------------------- Stress Peformance Test:
  console.log("------------STRESS PERFORMANCE TEST-----------------")
  await sleep(1000); // Sleep for an extra second before running the tests

  console.log(`Stress Test (${stressTestIterations} iterations):`);
  for (let i = 5; i > 0; i--) {
    console.log(`${i} seconds...`);
    await sleep(1000);
  }
  console.log("\nStress Tests are starting now.\n");
  await sleep(1000); // Sleep for an extra second before running the tests

  // Measure execution time for Simplified function (no type checking)
  console.time('exampleSimpleFunction');
  const avgTimeSimpleFunction = measureExecutionTime(exampleSimpleFunction, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('exampleSimpleFunction');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Original "type" function (no cache)
  console.time('exampleTypeFunctionNoCache');
  const avgTimeTypeFunctionNoCache = measureExecutionTime(exampleTypeFunctionNoCache, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('exampleTypeFunctionNoCache');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Original "typeV2" function (no cache)
  console.time('exampleTypeFunctionNoCacheV2');
  const avgTimeTypeFunctionNoCacheV2 = measureExecutionTime(exampleTypeFunctionNoCacheV2, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('exampleTypeFunctionNoCacheV2');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "type" function
  console.time('exampleTypeFunctionWithCache');
  const avgTimeTypeFunctionWithCache = measureExecutionTime(exampleTypeFunctionWithCache, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('exampleTypeFunctionWithCache');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "typeV2" function
  console.time('exampleTypeFunctionWithCacheV2');
  const avgTimeTypeFunctionWithCacheV2 = measureExecutionTime(exampleTypeFunctionWithCacheV2, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('exampleTypeFunctionWithCacheV2');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "typeV3" function
  console.time('exampleTypeV3');
  const avgTimeTypeV3 = measureExecutionTime(exampleTypeV3, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('exampleTypeV3');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // ----------------------------------------------------- WarmU[ ]Stress Peformance Test:
  console.log("------------WARMING UP STRESS PERFORMANCE TEST-----------------")
  await sleep(1000); // Sleep for an extra second before running the tests

  console.log(`Warm Up Stress Test (${stressTestIterations} iterations):`);
  for (let i = 5; i > 0; i--) {
    console.log(`${i} seconds...`);
    await sleep(1000);
  }
  console.log("\nWarm Up Stress Tests are starting now.\n");
  await sleep(1000); // Sleep for an extra second before running the tests

  // Measure execution time for Simplified function (no type checking)
  console.time('WarmUpExampleSimpleFunction');
  measureExecutionTime(exampleSimpleFunction, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleSimpleFunction');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Original "type" function (no cache)
  console.time('WarmUpExampleTypeFunctionNoCache');
  measureExecutionTime(exampleTypeFunctionNoCache, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionNoCache');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Original "typeV2" function (no cache)
  console.time('WarmUpExampleTypeFunctionNoCacheV2');
  measureExecutionTime(exampleTypeFunctionNoCacheV2, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionNoCacheV2');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "type" function
  console.time('WarmUpExampleTypeFunctionWithCache');
  measureExecutionTime(exampleTypeFunctionWithCache, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionWithCache');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "typeV2" function
  console.time('WarmUpExampleTypeFunctionWithCacheV2');
  measureExecutionTime(exampleTypeFunctionWithCacheV2, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeFunctionWithCacheV2');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // Measure execution time for Modified "typeV3" function
  console.time('WarmUpExampleTypeV3');
  measureExecutionTime(exampleTypeV3, { a: 40, b: 2, c: 'Hello' }, stressTestIterations);
  console.timeEnd('WarmUpExampleTypeV3');
  await sleep(5000); // Sleep for 5 seconds before the next test

  // ----------------------------------------------------- Determine the fastest function before and after the stress test
  await sleep(1000); // Sleep for a second before finishing up
  console.log("-------------------------------------------------------- Finished:")
  console.log(`- Average execution time for Simplified function (no type checking): ${avgTimeSimpleFunction.time}ms - AVG Total: ${avgTimeSimpleFunction.time * stressTestIterations}s - Proof: ${avgTimeSimpleFunction.proof}`);
  console.log(`- Average execution time for Original "type" function (no cache): ${avgTimeTypeFunctionNoCache.time}ms - AVG Total: ${avgTimeTypeFunctionNoCache.time * stressTestIterations}s - Proof: ${avgTimeTypeFunctionNoCache.proof}`);
  console.log(`- Average execution time for Original "typeV2" function (no cache): ${avgTimeTypeFunctionNoCacheV2.time}ms - AVG Total: ${avgTimeTypeFunctionNoCacheV2.time * stressTestIterations}s - Proof: ${avgTimeTypeFunctionNoCacheV2.proof}`);
  console.log(`- Average execution time for Modified "type" function (with cache): ${avgTimeTypeFunctionWithCache.time}ms - AVG Total: ${avgTimeTypeFunctionWithCache.time * stressTestIterations}s - Proof: ${avgTimeTypeFunctionWithCache.proof}`);
  console.log(`- Average execution time for Modified "typeV2" function (with cache): ${avgTimeTypeFunctionWithCacheV2.time}ms - AVG Total: ${avgTimeTypeFunctionWithCacheV2.time * stressTestIterations}s - Proof: ${avgTimeTypeFunctionWithCacheV2.proof}`);
  console.log(`- Average execution time for Modified "typeV3" function: ${avgTimeTypeV3.time}ms - AVG Total: ${avgTimeTypeV3.time * stressTestIterations}s - Proof: ${avgTimeTypeV3.proof}`);
  console.log("---------------")
  await sleep(1000); // Sleep for an extra second before running the tests
  const { fastestName, fastestTime } = getFastestMethod(avgTimeTypeFunctionNoCache.time, avgTimeTypeFunctionNoCacheV2.time, avgTimeTypeFunctionWithCache.time, avgTimeTypeFunctionWithCacheV2.time, avgTimeTypeV3.time);
  console.log(`The fastest method, except Simplified, is: ${fastestName} with an execution time of ${fastestTime} milliseconds`);
  console.log("\nAll tests completed.");
}

// ----------------------------------------------------- Test Helpers:

// Helper function to sleep for a specified duration
function getFastestMethod(
  noCacheTime,
  noCacheTimeV2,
  withCacheTime,
  withCacheTimeV2,
  V3
) {
  const times = [
    { name: "Original 'type' function (no cache)", time: noCacheTime },
    { name: "Original 'type' function V2 (no cache)", time: noCacheTimeV2 },
    { name: "'type' function with cache", time: withCacheTime },
    { name: "'type' function V2 with cache", time: withCacheTimeV2 },
    { name: "'type' function V3", time: V3 },
  ];

  const fastestTime = Math.min(...times.map((t) => t.time));

  return { fastestName: times.find((t) => t.time === fastestTime).name, fastestTime: fastestTime };
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Function to measure execution time with averaging
function measureExecutionTime(func, args, iterations = 1000) {
  let totalTime = 0;
  let proof = 0;
  for (let i = 0; i < iterations; i++) {
    const startTime = performance.now();
    proof += func(args);
    const endTime = performance.now();
    totalTime += endTime - startTime;
  }

  return {time:totalTime / iterations, proof}; // Calculate the average execution time
}

// Start running the tests
console.log("Starting tests...\n");
runTests();
YaakovZ1 commented 1 year ago

This is pure gold! Catching errors when types don't match up? It's the kind of thing that saves devs countless hours of debugging and facepalming. Kudos to Malfunctionify for emphasizing this!

Alright, let's face it: errors happen. But with clear messages and original line numbers? It feels like Malfunctionify is handing us a map to the bug treasure. And spotting multiple type errors at once? It's like a developer's early Christmas. Huge shoutout to Malfunctionify for bringing this into the light. It's projects like these that keep the dev world spinning and evolving. I'm eagerly waiting to see where this journey goes.

Cheers to innovation and pushing boundaries! 🚀

Nick-Riggs commented 1 year ago

This is run-time parameter checking. IMO, it's quite different than adding type annotations to the JavaScript language in some way.

Also, there are quite robust solutions for this already. Zod, JSON Schema, Joi, etc.