Open
Description
Search Terms
type check statement compile (compilation) time
Suggestion
I wanted to check compatibilities for types from different (especially external) modules in compilation time.
import { SomeType } from 'some-external';
// ... something statement here to check if SomeType is not changed
// by update for 'some-external' or etc.
To solve it, I suggest to add type-check statement like following:
// Compile error if 'SomeType' does not satisfy the constraint 'WantedType'
// (similar to error TS2344)
type assert SomeType extends WantedType;
// The message "Unexpected 'SomeType'" will be output if the error occurs
// - The trailing expression (message clause) must be a valid string literal
// (the message clause is not necessary for this suggestion
// but would make easier to solve the error...)
type assert SomeType extends WantedType, "Unexpected 'SomeType'";
- The type-check process should be same as the process for type constraints in type parameters.
- There is already
type
statement, but the above statements are not conflicted becausetype
statements requires=
after BindingIdentifier (or TypeParameters). - Similar to [Proposal] Type assertion statement (type cast) at block-scope level #10421, but this
type assert
only check types; it does not cast variables. extends
keyword may confuse to one used in conditional type. (not ambiguous?)- For
type assert A extends B
, bothA
andB
should accept conditional type.
- For
- (
type assert
is somewhat inspired by other languages'assert
statements (especiallystatic_assert
in C/C++), but I don't know whether the wordstype assert
are the best...)
Use Cases
- Check compatibilities for types simply and easily
- Useful to avoid using incompatible package versions with another packages/modules.
- Assert types explicitly with more human-readable
- This would not be useful for compilers/implementers, but would be useful for developers to read codes.
- Test type definition (e.g. for unit test)
Currently we can check types statically as following:
import { SomeType } from 'some-external';
// Helper definition to check type
type TypeCheckerOfWantedType<T extends WantedType> = T;
// Error if 'SomeType' does not satisfy 'WantedType'
type CheckResultForSomeType = TypeCheckerOfWantedType<SomeType>;
// this is necessary to avoid "'CheckResultForSomeType' is declared but never used"
declare global { var _dummyVariableForCheckSomeType: CheckResultForSomeType; }
While no JavaScript codes are generated from this code, this is more complex to check.
Examples
// Must not be an error
type assert Element extends Node;
// Error in 'es5', pass in 'es2015'
type assert ObjectConstructor extends { assign: (...args: any[]) => any; },
'Object.assign() is not available';
import { User } from 'db-library';
// Error if User does not have 'id' member with type assignable to 'number'
type assert User extends { id: number; }, 'Unexpected User type';
const a = someFunction();
if (typeof a === 'string') {
// treat 'a' as 'string'
} else {
// Error if narrowed type of 'a' does not have '{ data: string }'
type assert typeof a extends { data: string; };
// (this assert does not mean that type of 'a' is treated as '{ data: string }' here)
}
Checklist
My suggestion meets these guidelines:
- 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, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.