Typescript: Proposal: Type Relationship API

Created on 22 Jul 2016  路  19Comments  路  Source: microsoft/TypeScript

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).

Proposal

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.

API In Discussion Suggestion

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.

All 19 comments

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:

  1. tslint rule no-inferrable-type

where only primitive number, string and boolean types are supported

  1. tslint-consistent-codestyle no-unnecessary-type-annotation (@ajafff)
    where typeToString 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 }
  2. fimbullinter/wotan no-useless-assertion and prefer-for-of
  3. 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.

image

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,

image

@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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MartynasZilinskas picture MartynasZilinskas  路  3Comments

siddjain picture siddjain  路  3Comments

CyrusNajmabadi picture CyrusNajmabadi  路  3Comments

weswigham picture weswigham  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments