Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add strictFunctionTypes to Enhance Type Safety for Function Parameters #58607

Closed
6 tasks done
KevBeltrao opened this issue May 21, 2024 · 3 comments
Closed
6 tasks done

Comments

@KevBeltrao
Copy link

πŸ” Search Terms

strict function types TypeScript
TypeScript array union type safety
TypeScript specific to union array assignment
TypeScript union type array assignment error

βœ… Viability Checklist

⭐ Suggestion

Introduce a new compiler option, strictFunctionTypes, to enhance type safety for function parameters. This option would prevent assigning specific array types (e.g., string[]) to function parameters expecting broader union types (e.g., (string | number)[]). This change aims to avoid potential runtime errors caused by unintended type mutations, ensuring that arrays maintain their intended types when passed to functions. It'd be a non-breaking change since it'd need to explicitly adding this option to tsconfig.

// TypeScript Configuration (tsconfig.json)
{
  "compilerOptions": {
    "strictFunctionTypes": true // Hypothetical option for stricter checking
  }
}

πŸ“ƒ Motivating Example

Imagine you have a function that accepts an array of either strings or numbers and modifies it. Currently, TypeScript allows you to pass an array of strings to this function, potentially leading to runtime errors when the function adds a number to the array. With the proposed strictFunctionTypes option, TypeScript would prevent this assignment, ensuring type safety and avoiding unexpected mutations.

type StringOrNumber = string | number;

const listOfStrings: string[] = [];

const addToListOfStrings = (list: StringOrNumber[]) => {
  list.push(1); // Adds a number to the list of strings
}

addToListOfStrings(listOfStrings); // Currently allowed, but causes a runtime error

πŸ’» Use Cases

  1. What do you want to use this for?
    API Design and Validation:
    When designing APIs or library functions that accept a union of types, it's crucial to enforce strict type constraints. By not allowing specific array types (e.g., string[]) to be passed to parameters expecting broader union types (e.g., (string | number)[]), developers can ensure that functions handle inputs in a type-safe manner, avoiding unintended mutations and runtime errors.
  2. What shortcomings exist with current approaches?
    Lack of Strict Type Enforcement:
    Currently, TypeScript's type system is flexible, allowing specific types to be passed to parameters expecting union types. This flexibility can lead to unintended side effects, such as modifying an array of strings with a number, which contradicts the intended type of the array. This shortcoming can result in runtime errors and makes it harder to maintain type safety in large or collaborative codebases.
  3. What workarounds are you using in the meantime?
    API Design and Validation:
    To prevent type-related errors, developers often add manual runtime checks within functions to ensure that arrays contain only the expected types before performing operations. While this approach helps avoid some issues, it adds boilerplate code and relies on runtime checks rather than leveraging TypeScript's compile-time type system.
    Example:
const addToListOfStrings = (list: (string | number)[]) => {
  if (list.every(item => typeof item === 'string')) {
    list.push('new string'); // Safe to add a string
  } else {
    throw new Error('List contains non-string elements');
  }
}

Use of generics
Another workaround is to use generics to enforce type constraints more explicitly. However, this approach can become complex and cumbersome for simple use cases.

const listOfStrings: string[] = [];

const addToList = <T extends string | number>(list: T[], item: T): void => {
  list.push(item);
}

addToList(listOfStrings, 'new string'); // Safe
addToList(listOfStrings, 1); // Type error

By implementing the strictFunctionTypes option, TypeScript could automatically enforce these constraints at compile time, reducing the need for manual checks and making the codebase more robust and maintainable.

@fatcerberus
Copy link

strictFunctionTypes already exists and it does something completely different than what you're suggesting.

@fatcerberus
Copy link

It looks like you're really looking for something like #14150; the unsoundness described here isn't limited to function calls.

@KevBeltrao
Copy link
Author

In this case, I'm moving this discussion to that issue! Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants