Repository navigation
Add strictFunctionTypes to Enhance Type Safety for Function Parameters #58607
Description
🔍 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
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ 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
- 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. - 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. - 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 errorBy 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.
Activity
fatcerberus commentedon May 21, 2024
strictFunctionTypesalready exists and it does something completely different than what you're suggesting.fatcerberus commentedon May 21, 2024
It looks like you're really looking for something like #14150; the unsoundness described here isn't limited to function calls.
KevBeltrao commentedon May 21, 2024
In this case, I'm moving this discussion to that issue! Thanks