microsoft / microsoft/TypeScript
Permit functions that return a value to also serve as a type guard
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
Search Terms
Linear type, affine type, type guard
Suggestion
It would be very helpful to allow a function to serve as a type guard, but also return an unrelated value.
Use Cases
This can be used to express type changes as a result of mutating operations, covering some of the use cases of e.g. Rust's affine types. (See also #16148.)
Examples
Consider this example, compiled with --strictNullChecks:
type NonEmptyArray<T> = {
pop(): T;
} & Array<T>;
function isNonEmpty<T>(array: Array<T>): array is NonEmptyArray<T>;
function isNonEmpty(array: Array<unknown>): boolean {
return array.length > 0;
}
let array: string[] = ['element'];
if (isNonEmpty(array)) { // Guard gives 'array' type NonEmptyArray<string>.
const elem1: string = array.pop(); // Works. This is correct.
const elem2: string = array.pop(); // Also works, but elem2 will be undefined at runtime!
}
We could make this correct if pop() could both return a value and behave as a type guard. This isn't great syntax, but nonetheless consider if this was supported:
type NonEmptyArray<T> = {
pop(): T && this is Array<T>;
} & Array<T>;
function isNonEmpty<T>(array: Array<T>): array is NonEmptyArray<T>;
function isNonEmpty(array: Array<unknown>): boolean {
return array.length > 0;
}
let array: string[] = ['element'];
if (isNonEmpty(array)) { // Guard gives 'array' type NonEmptyArray<string>.
const elem1: string = array.pop(); // Returns a string and gives 'array' type Array<string>.
const elem2: string = array.pop(); // Doesn't compile; pop() returns 'string | undefined'!
}
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
This can use a new, previously invalid syntax to avoid affecting any existing program.
- 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, etc.)
There's no change in the code that's emitted; this feature would exist purely at the level of the type system.
- This feature would agree with the rest of TypeScript's Design Goals.
I believe that it would. It seems to be aligned well, in particular, with "Statically identify constructs that are likely to be errors."
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The issue does not name a source file, test, or compiler entry point. Begin by locating TypeScript's handling of type predicates and return types, then define the accepted syntax and narrowing behavior, including the mutating pop() example, with compiler tests covering the new and invalid cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100