Skip to content

Commit

Permalink
Only return the substitute in substitution instantiation when assigna…
Browse files Browse the repository at this point in the history
…bility fails (rather than subtype) (microsoft#31027)

* Only return the substitute in substitution instantiation when assignability fails (rather than subtype)

* Add test from issue
  • Loading branch information
weswigham authored Apr 19, 2019
1 parent 40a2eb2 commit 7a3e68f
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11295,7 +11295,7 @@ namespace ts {
}
else {
const sub = instantiateType((<SubstitutionType>type).substitute, mapper);
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeSubtypeOf(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) {
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) {
return maybeVariable;
}
return sub;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//// [substitutionTypeNoMergeOfAssignableType.ts]
interface Entry {
comment?: string;
}

interface Entity {
fields: {[key: string]: Entry};
}

type Fields<E extends Entity> = {
[P in keyof E["fields"]]: E["fields"][P]
};

type Nodes<T = any> = {
[P in keyof T]: T[P] extends Entity
? Fields<T[P]>
: T[P]
};

function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
return {} as Nodes<T>
}

const myTest = makeEntityStore({ test: { fields: { id: {} } } });
myTest.test


//// [substitutionTypeNoMergeOfAssignableType.js]
function makeEntityStore(config) {
return {};
}
var myTest = makeEntityStore({ test: { fields: { id: {} } } });
myTest.test;
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
=== tests/cases/compiler/substitutionTypeNoMergeOfAssignableType.ts ===
interface Entry {
>Entry : Symbol(Entry, Decl(substitutionTypeNoMergeOfAssignableType.ts, 0, 0))

comment?: string;
>comment : Symbol(Entry.comment, Decl(substitutionTypeNoMergeOfAssignableType.ts, 0, 17))
}

interface Entity {
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))

fields: {[key: string]: Entry};
>fields : Symbol(Entity.fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 4, 19))
>key : Symbol(key, Decl(substitutionTypeNoMergeOfAssignableType.ts, 5, 15))
>Entry : Symbol(Entry, Decl(substitutionTypeNoMergeOfAssignableType.ts, 0, 0))
}

type Fields<E extends Entity> = {
>Fields : Symbol(Fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 6, 2))
>E : Symbol(E, Decl(substitutionTypeNoMergeOfAssignableType.ts, 8, 13))
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))

[P in keyof E["fields"]]: E["fields"][P]
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 9, 6))
>E : Symbol(E, Decl(substitutionTypeNoMergeOfAssignableType.ts, 8, 13))
>E : Symbol(E, Decl(substitutionTypeNoMergeOfAssignableType.ts, 8, 13))
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 9, 6))

};

type Nodes<T = any> = {
>Nodes : Symbol(Nodes, Decl(substitutionTypeNoMergeOfAssignableType.ts, 10, 3))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))

[P in keyof T]: T[P] extends Entity
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))

? Fields<T[P]>
>Fields : Symbol(Fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 6, 2))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))

: T[P]
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 12, 12))
>P : Symbol(P, Decl(substitutionTypeNoMergeOfAssignableType.ts, 13, 6))

};

function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
>makeEntityStore : Symbol(makeEntityStore, Decl(substitutionTypeNoMergeOfAssignableType.ts, 16, 3))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>Entity : Symbol(Entity, Decl(substitutionTypeNoMergeOfAssignableType.ts, 2, 2))
>config : Symbol(config, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 60))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))
>Nodes : Symbol(Nodes, Decl(substitutionTypeNoMergeOfAssignableType.ts, 10, 3))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))

return {} as Nodes<T>
>Nodes : Symbol(Nodes, Decl(substitutionTypeNoMergeOfAssignableType.ts, 10, 3))
>T : Symbol(T, Decl(substitutionTypeNoMergeOfAssignableType.ts, 18, 26))
}

const myTest = makeEntityStore({ test: { fields: { id: {} } } });
>myTest : Symbol(myTest, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 6))
>makeEntityStore : Symbol(makeEntityStore, Decl(substitutionTypeNoMergeOfAssignableType.ts, 16, 3))
>test : Symbol(test, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 33))
>fields : Symbol(fields, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 41))
>id : Symbol(id, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 51))

myTest.test
>myTest.test : Symbol(test, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 33))
>myTest : Symbol(myTest, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 6))
>test : Symbol(test, Decl(substitutionTypeNoMergeOfAssignableType.ts, 22, 33))

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
=== tests/cases/compiler/substitutionTypeNoMergeOfAssignableType.ts ===
interface Entry {
comment?: string;
>comment : string
}

interface Entity {
fields: {[key: string]: Entry};
>fields : { [key: string]: Entry; }
>key : string
}

type Fields<E extends Entity> = {
>Fields : Fields<E>

[P in keyof E["fields"]]: E["fields"][P]
};

type Nodes<T = any> = {
>Nodes : Nodes<T>

[P in keyof T]: T[P] extends Entity
? Fields<T[P]>
: T[P]
};

function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
>makeEntityStore : <T extends Record<string, Entity>>(config: T) => Nodes<T>
>config : T

return {} as Nodes<T>
>{} as Nodes<T> : Nodes<T>
>{} : {}
}

const myTest = makeEntityStore({ test: { fields: { id: {} } } });
>myTest : Nodes<{ test: { fields: { id: {}; }; }; }>
>makeEntityStore({ test: { fields: { id: {} } } }) : Nodes<{ test: { fields: { id: {}; }; }; }>
>makeEntityStore : <T extends Record<string, Entity>>(config: T) => Nodes<T>
>{ test: { fields: { id: {} } } } : { test: { fields: { id: {}; }; }; }
>test : { fields: { id: {}; }; }
>{ fields: { id: {} } } : { fields: { id: {}; }; }
>fields : { id: {}; }
>{ id: {} } : { id: {}; }
>id : {}
>{} : {}

myTest.test
>myTest.test : Fields<{ fields: { id: {}; }; }>
>myTest : Nodes<{ test: { fields: { id: {}; }; }; }>
>test : Fields<{ fields: { id: {}; }; }>

25 changes: 25 additions & 0 deletions tests/cases/compiler/substitutionTypeNoMergeOfAssignableType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interface Entry {
comment?: string;
}

interface Entity {
fields: {[key: string]: Entry};
}

type Fields<E extends Entity> = {
[P in keyof E["fields"]]: E["fields"][P]
};

type Nodes<T = any> = {
[P in keyof T]: T[P] extends Entity
? Fields<T[P]>
: T[P]
};

function makeEntityStore<T extends Record<string, Entity>>(config: T): Nodes<T> {
return {} as Nodes<T>
}

const myTest = makeEntityStore({ test: { fields: { id: {} } } });
myTest.test

0 comments on commit 7a3e68f

Please sign in to comment.