They type checker has no public API for checking type relationships or for looking up types in a given scope. This can make writing semantic-analysis informed lint rules very difficult (either because you need to replicate a lot of logic, or because you simply don't have enough information). Since tslint enabled semantic checks we now have a popular consumer who would very much like to be able to perform meaningful semantic comparisons (See this rule for instance).
A small new set of public API methods on the type checker:
interface TypeChecker {
isIdenticalTo(a: Type, b: Type): boolean; // Exposes internal identity relationship
isSubtypeOf(a: Type, b: Type): boolean; // Exposes internal structural subtype relationship
isAssignableTo(a: Type, b: Type): boolean; // Exposes internal assignable relationship
isComparableTo(a: Type, b: Type): boolean; // Exposes internal comparable relationship
isInstantiationOf(a: GenericType, b: GenericType): boolean; // While not an internal relationship, it is a straightforward semantic question to ask an answer which requires internal APIs to answer
lookupGlobalType(name: string): Type; // Looks up a global symbol named "name" with meaning SymbolFlags.Type (Identical to getGlobalType but without the expected arity based return value) - returns unknownType on failure
lookupTypeAt(name: string, position: Node): Type // Resolve a symbol lexically at the position specified with meaning SymbolFlags.Type (identical to resolveName, but with less levers) - returns unknownType on failure
getAnyType(): IntrinsicType;
getStringType(): IntrinsicType;
getNumberType(): IntrinsicType;
getBooleanType(): IntrinsicType;
getVoidType(): IntrinsicType;
getUndefinedType(): IntrinsicType;
getNullType(): IntrinsicType;
getESSymbolType(): IntrinsicType;
getNeverType(): IntrinsicType;
getUnknownType(): IntrinsicType;
getStringLiteralType(text: string): StringLiteralType; // When numeric literal types merge, they should have a similar endpoint
}
While this isn't a complete set of APIs for manipulating the type checker (I've left off an API to programatically create new types for comparison), it is enough to do meaningful analysis with existing types in the compilation without resorting to brittle (or often wrong) syntax-based solutions.
cc: @ahejlsberg @mhegazy @rbuckton @vladima
I was discussing something like this today with @novemberborn in that sometimes we aren't sure why the types are what they are at a particular point, especially when they are contextually inferred.
Would it be possible to also consider potentially offering up additional meta data that would explain the type at a particular node (sort of like good ole SQL "explain plan")?
Would it be possible to also consider potentially offering up additional meta data that would explain the type at a particular node (sort of like good ole SQL "explain plan")?
can you elaborate on the scenario here? there are multiple ways the compiler can find a type. though, all is specified by the spec, the information is not persisted any ways. what we know is a type.
Is there any progress on this? It would be incredibly useful to have. Just an isTypeAssignableTo
would be great. Right now the only option is to link to a customly patched version of TypeScript.
What's the status of this API? It is needed for checking yields in redux-saga (described in #19602)
Agree with @samvv - isTypeAssignableTo
would be enough to check lhr and rhs of a yield expression with a custom type-based comparison which is needed sooo badly.
UPD: Have found #9943
As PR https://github.com/Microsoft/TypeScript/pull/9943 is closed now, is it possible to export only isTypeRelatedTo
and *Relation
which should cover @raveclassic and @samvv cases, also with identityRelation
it will cover:
where only primitive number, string and boolean types are supported
- tslint-consistent-codestyle no-unnecessary-type-annotation (@ajafff)
wheretypeToString
is used, however it often gives false negatives, e.g. in cases like{ x: number, y: number }
type is not equal to{ y: number, x: number }
- fimbullinter/wotan no-useless-assertion and prefer-for-of
- no-inferrable-return-types
also it will cover few closed issues, where authors were waiting for PR https://github.com/Microsoft/TypeScript/pull/9943 like:
also at least partly it should cover this request: https://github.com/Microsoft/TypeScript/issues/11728#issuecomment-257023378
also it will close a few more closed by bot issues.
@weswigham @mhegazy
As another use case, this would enable type checking HTML template systems such as lit-html. For this code:
html`<date-picker .date=${currentDate}></date-picker>`
We'd want to assert that typeof currentDate
is assignable to ElementTagNameMap['date-picker'].date
.
This is applicable to many template systems.
Would love to see some baby steps that could occur in this area. It seems like the tooling that would be enabled by having APIs like this would be tremendous! As @ahejlsberg pointed out, it's hard to know where to stop with these APIs, but I liked @weswigham's suggestion in the original pull request #9943 about separating the "type comparison" apis from the "type building" apis. It seems like the "type building" apis is a big task, but the type comparison apis might be a smaller effort?
Just chatted with @octref about improving the custom element editing experience. I think the need for these APIs is showing up across the editing ecosystem as every template system provider eventually runs into the need to type check templates.
He pointed out that the Vue LSP is working on implementing type checking of templates by doing code generation, running the typescript compiler over the generated code, then mapping diagnostics on that code back to the original source: https://github.com/vuejs/vetur/pull/681/files
Having a type relationship API would make this sort of work simpler, faster, and more robust.
I've just discovered https://www.npmjs.com/package/ts-simple-type by way of https://twitter.com/RuneMehlsen/status/1095401856493330433.
It looks like a reasonable alternative until the official TS api is public.
I was trying to write a lint rule where the following would not be allowed,
declare const x : { foo : string };
//Should have lint error
const y : { foo : string|number } = x;
y.foo = 42;
console.log(x.foo);
And had to write a lot of type checking code that's probably super brittle.
I'll give ts-simple-type a whirl
[EDIT]
I just tried ts-simple-type
and it... Crashes a lot.
It exceeds the max call stack size regularly and also runs into,
Couldn't find 'typeParameter' for type 'FUNCTION'
When it doesn't crash, it works great, though.
Here's an example of where typescript-eslint
fails because it is trying to (poorly) emulate TS' type checking.
It doesn't realize that
(RawExprT & false) | (RawExprT & true)
is assignable to
boolean
After assigning rawExpr
to a boolean
variable, it works fine,
@AnyhowStep if you have reasonably-sized repros please file issues on ts-simple-type for any crashes you find
I'm wondering what the status is on this proposal. Something like isAssignableTo(a: Type, b: Type): boolean;
would really help me out on a project I'm working on. I'm using ts-simple-type
, but it fails to calculate assignability for RxJS Observables. Created an issuer in the repo https://github.com/runem/ts-simple-type/issues/54.
It's exposed in internal type checker API now: https://github.com/microsoft/TypeScript/pull/33263/files#diff-c3ed224e4daa84352f7f1abcd23e8ccaR525-R527
It's not publicly documented, is it?
Lol, nope. It's not public at all.
I need a crying emoji
We need this. Please add this.
Most helpful comment
I've just discovered https://www.npmjs.com/package/ts-simple-type by way of https://twitter.com/RuneMehlsen/status/1095401856493330433.
It looks like a reasonable alternative until the official TS api is public.