Skip to content

A extends B relation break after wrapping by extra interface #39549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
nzhl opened this issue Jul 10, 2020 · 4 comments
Closed

A extends B relation break after wrapping by extra interface #39549

nzhl opened this issue Jul 10, 2020 · 4 comments
Assignees
Labels
Bug A bug in TypeScript Rescheduled This issue was previously scheduled to an earlier milestone

Comments

@nzhl
Copy link

nzhl commented Jul 10, 2020

TypeScript Version: 3.9.2

Search Terms: subtypes, extends

Expected behavior: {name: "age"} extends {name: string} so A extends B

Code

export type IsFlatObject<T extends object> = Extract<
  Exclude<T[keyof T],  Date | FileList>,
  any[] | object
> extends never
  ? true
  : false;

export type FieldValues = Record<string, any>;

export type FieldName<TFieldValues extends FieldValues> = IsFlatObject<
  TFieldValues
> extends true
  ? Extract<keyof TFieldValues, string>
    : string;


export type CustomElement<TFieldValues> = {
    name: FieldName<TFieldValues>;
};


type ISChild<A, B> = A extends B ? true : false

type A = CustomElement<{age: string}>  // i.e {name: "age"}  
type B = CustomElement<FieldValues>   //  i.e {name: string}
type ShouldBeTrue = ISChild<{name: "age"}, {name: string}>   // type true 
type ShouldBeTrueAsWell = ISChild<A, B> // type false 

Output
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2017",
    "jsx": "React",
    "module": "ESNext"
  }
}

Playground Link: Provided

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Jul 15, 2020
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 4.1 milestone Jul 15, 2020
@RyanCavanaugh
Copy link
Member

Regressed sometime between 3.3 and 3.5

@jamiehaywood
Copy link

is there any update on this?

@RyanCavanaugh RyanCavanaugh added the Rescheduled This issue was previously scheduled to an earlier milestone label Dec 11, 2020
@weswigham
Copy link
Member

So, the example as given is fixed, but not for the right reasons. Now A and B have separate aliases - A and B, respectively, so we no longer use the variance result to compare them. If you change the example to

type ShouldBeTrueAsWell = ISChild<CustomElement<{age: string}>, CustomElement<FieldValues>> // type false

the issue still repros. The root cause is that we measure all of IsFlatObject, FieldName, and CustomElement as solely covariant on their type parameters. This is incorrect. Root-cause-wise, this is a dupe of #31295. We incorrectly measure the variance of a conditional type checkType clause. While the conditional may be covariant on a parameter within the check type (or actually bivariant, by our current relationship rules), the result of evaluating the condition, the true or false type, may be entirely unrelated, and may vary entirely differently with the type parameter in question.

The fix is simple to put together - mark conditional check type positions as Unmeasurable variance. The issue, however, is that this makes us fall back to structural comparisons when comparing many conditional instantiations which we previously shortcut (albeit potentially incorrectly) - a notable example is our own conditionalTypeDoesntSpinForever test which shows that doing that alone re-breaks the issue which #27804 (the introduction of alias measurement on conditionals directly) originally fixed, making us OOM on the test.

@RyanCavanaugh
Copy link
Member

I think the OP being fixed is sufficient to close this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Rescheduled This issue was previously scheduled to an earlier milestone
Projects
None yet
Development

No branches or pull requests

5 participants