Skip to content
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

Improve soundness of indexed access types #30769

Merged
merged 22 commits into from
Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 178 additions & 126 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7784,7 +7784,7 @@ namespace ts {
const referencedFiles = context.referencedFiles;
const typeReferenceDirectives = context.typeReferenceDirectives;
const libReferenceDirectives = context.libReferenceDirectives;
forEach(toArray(entryOrList), (arg: PragmaPseudoMap["reference"]) => {
forEach(toArray(entryOrList) as PragmaPseudoMap["reference"][], arg => {
const { types, lib, path } = arg.arguments;
if (arg.arguments["no-default-lib"]) {
context.hasNoDefaultLib = true;
Expand All @@ -7806,8 +7806,8 @@ namespace ts {
}
case "amd-dependency": {
context.amdDependencies = map(
toArray(entryOrList),
(x: PragmaPseudoMap["amd-dependency"]) => ({ name: x.arguments.name, path: x.arguments.path }));
toArray(entryOrList) as PragmaPseudoMap["amd-dependency"][],
x => ({ name: x.arguments.name, path: x.arguments.path }));
break;
}
case "amd-module": {
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4279,7 +4279,8 @@ namespace ts {
objectType: Type;
indexType: Type;
constraint?: Type;
simplified?: Type;
simplifiedForReading?: Type;
simplifiedForWriting?: Type;
}

export type TypeVariable = TypeParameter | IndexedAccessType;
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2359,7 +2359,8 @@ declare namespace ts {
objectType: Type;
indexType: Type;
constraint?: Type;
simplified?: Type;
simplifiedForReading?: Type;
simplifiedForWriting?: Type;
}
type TypeVariable = TypeParameter | IndexedAccessType;
interface IndexType extends InstantiableType {
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2359,7 +2359,8 @@ declare namespace ts {
objectType: Type;
indexType: Type;
constraint?: Type;
simplified?: Type;
simplifiedForReading?: Type;
simplifiedForWriting?: Type;
}
type TypeVariable = TypeParameter | IndexedAccessType;
interface IndexType extends InstantiableType {
Expand Down

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions tests/baselines/reference/infiniteConstraints.errors.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
tests/cases/compiler/infiniteConstraints.ts(3,37): error TS2589: Type instantiation is excessively deep and possibly infinite.
tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
tests/cases/compiler/infiniteConstraints.ts(21,21): error TS2536: Type '"val"' cannot be used to index type 'Extract<T[K], Record<"val", string>>'.
tests/cases/compiler/infiniteConstraints.ts(21,57): error TS2536: Type '"val"' cannot be used to index type 'Extract<T[Exclude<keyof T, K>], Record<"val", string>>'.
Expand All @@ -7,12 +6,10 @@ tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: Type 'Record<"
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.


==== tests/cases/compiler/infiniteConstraints.ts (7 errors) ====
==== tests/cases/compiler/infiniteConstraints.ts (6 errors) ====
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint

type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
type T2<B extends { [K in keyof B]: B[Exclude<keyof B, K>]["val"] }> = B;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/keyofAndIndexedAccess.types
Original file line number Diff line number Diff line change
Expand Up @@ -1794,7 +1794,7 @@ function updateIds2<T extends { [x: string]: string }, K extends keyof T>(
>key : K

stringMap[x]; // Should be OK.
>stringMap[x] : { [oldId: string]: string; }[T[K]]
>stringMap[x] : string
>stringMap : { [oldId: string]: string; }
>x : T[K]
}
Expand Down
190 changes: 190 additions & 0 deletions tests/baselines/reference/keyofAndIndexedAccess2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(4,5): error TS2322: Type '"x"' is not assignable to type 'number'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(6,5): error TS2322: Type '2' is not assignable to type '0 | 1'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(7,5): error TS2322: Type '"x"' is not assignable to type '0 | 1'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(8,5): error TS2322: Type '1' is not assignable to type 'never'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(9,5): error TS2322: Type '2' is not assignable to type 'never'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(10,5): error TS2322: Type '"x"' is not assignable to type 'never'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(14,5): error TS2739: Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(15,5): error TS2322: Type 'T' is not assignable to type '{ x: number; y: number; }'.
Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(18,5): error TS2322: Type '{ x: number; y: number; }' is not assignable to type 'T'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(19,5): error TS2322: Type '{ [key: string]: number; }' is not assignable to type 'T'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(26,7): error TS2339: Property 'x' does not exist on type 'T'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(27,5): error TS2322: Type '1' is not assignable to type 'T[keyof T]'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(31,5): error TS2322: Type '{ [key: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(38,5): error TS2322: Type '{ [x: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(50,3): error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(51,3): error TS2322: Type '123' is not assignable to type 'string & number'.
Type '123' is not assignable to type 'string'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(52,3): error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(53,3): error TS2322: Type '123' is not assignable to type 'T[K]'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(65,7): error TS2339: Property 'foo' does not exist on type 'T'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(66,3): error TS2536: Type 'string' cannot be used to index type 'T'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(67,3): error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(68,3): error TS2322: Type '123' is not assignable to type 'T[K]'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts(108,5): error TS2322: Type '123' is not assignable to type 'Type[K]'.
Type '123' is not assignable to type '123 & "some string"'.
Type '123' is not assignable to type '"some string"'.


==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess2.ts (23 errors) ====
function f1(obj: { a: number, b: 0 | 1, c: string }, k0: 'a', k1: 'a' | 'b', k2: 'a' | 'b' | 'c') {
obj[k0] = 1;
obj[k0] = 2;
obj[k0] = 'x'; // Error
~~~~~~~
!!! error TS2322: Type '"x"' is not assignable to type 'number'.
obj[k1] = 1;
obj[k1] = 2; // Error
~~~~~~~
!!! error TS2322: Type '2' is not assignable to type '0 | 1'.
obj[k1] = 'x'; // Error
~~~~~~~
!!! error TS2322: Type '"x"' is not assignable to type '0 | 1'.
obj[k2] = 1; // Error
~~~~~~~
!!! error TS2322: Type '1' is not assignable to type 'never'.
obj[k2] = 2; // Error
~~~~~~~
!!! error TS2322: Type '2' is not assignable to type 'never'.
obj[k2] = 'x'; // Error
~~~~~~~
!!! error TS2322: Type '"x"' is not assignable to type 'never'.
}

function f2<T extends { [key: string]: number }>(a: { x: number, y: number }, b: { [key: string]: number }, c: T, k: keyof T) {
a = b; // Error, index signature in source doesn't imply properties are present
~
!!! error TS2739: Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
a = c; // Error, index signature in source doesn't imply properties are present
~
!!! error TS2322: Type 'T' is not assignable to type '{ x: number; y: number; }'.
!!! error TS2322: Type '{ [key: string]: number; }' is missing the following properties from type '{ x: number; y: number; }': x, y
b = a;
b = c;
c = a; // Error, constraint on target doesn't imply any properties or signatures
~
!!! error TS2322: Type '{ x: number; y: number; }' is not assignable to type 'T'.
c = b; // Error, constraint on target doesn't imply any properties or signatures
~
!!! error TS2322: Type '{ [key: string]: number; }' is not assignable to type 'T'.
a.x;
b.x;
c.x;
c[k];
a.x = 1;
b.x = 1;
c.x = 1; // Error, cannot write to index signature through constraint
~
!!! error TS2339: Property 'x' does not exist on type 'T'.
c[k] = 1; // Error, cannot write to index signature through constraint
~~~~
!!! error TS2322: Type '1' is not assignable to type 'T[keyof T]'.
}

function f3<K extends string>(a: { [P in K]: number }, b: { [key: string]: number }, k: K) {
a = b; // Error, index signature doesn't imply properties are present
~
!!! error TS2322: Type '{ [key: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
b = a;
a[k];
a[k] = 1;
}

function f3b<K extends string>(a: { [P in K]: number }, b: { [P in string]: number }, k: K) {
a = b; // Error, index signature doesn't imply properties are present
~
!!! error TS2322: Type '{ [x: string]: number; }' is not assignable to type '{ [P in K]: number; }'.
b = a;
}

function f4<K extends string>(a: { [key: string]: number }[K], b: number) {
a = b;
b = a;
}

type Item = { a: string, b: number };

function f10<T extends Item, K extends keyof T>(obj: T, k1: string, k2: keyof Item, k3: keyof T, k4: K) {
obj[k1] = 123; // Error
~~~~~~~
!!! error TS7017: Element implicitly has an 'any' type because type 'Item' has no index signature.
obj[k2] = 123; // Error
~~~~~~~
!!! error TS2322: Type '123' is not assignable to type 'string & number'.
!!! error TS2322: Type '123' is not assignable to type 'string'.
obj[k3] = 123; // Error
~~~~~~~
!!! error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
obj[k4] = 123; // Error
~~~~~~~
!!! error TS2322: Type '123' is not assignable to type 'T[K]'.
}

type Dict = Record<string, number>;

function f11<K extends keyof Dict>(obj: Dict, k1: keyof Dict, k2: K) {
obj.foo = 123;
obj[k1] = 123;
obj[k2] = 123;
}

function f12<T extends Readonly<Dict>, K extends keyof T>(obj: T, k1: keyof Dict, k2: keyof T, k3: K) {
obj.foo = 123; // Error
~~~
!!! error TS2339: Property 'foo' does not exist on type 'T'.
obj[k1] = 123; // Error
~~~~~~~
!!! error TS2536: Type 'string' cannot be used to index type 'T'.
obj[k2] = 123; // Error
~~~~~~~
!!! error TS2322: Type '123' is not assignable to type 'T[keyof T]'.
obj[k3] = 123; // Error
~~~~~~~
!!! error TS2322: Type '123' is not assignable to type 'T[K]'.
}

// Repro from #27895

export interface Entity {
id: number | string;
}

export type IdOf<E extends Entity> = E['id'];

export interface EntityState<E extends Entity> {
ids: IdOf<E>[];
entities: { [key: string]: E, [key: number]: E };
}


export function getAllEntities<E extends Entity>(state: EntityState<E>): E[] {
const { ids, entities } = state;
return ids.map(id => entities[id]);
}

export function getEntity<E extends Entity>(id: IdOf<E>, state: EntityState<E>): E | undefined {
const { ids, entities } = state;

if (!ids.includes(id)) {
return undefined;
}

return entities[id];
}

// Repro from #30603

interface Type {
a: 123;
b: "some string";
}

function get123<K extends keyof Type>(): Type[K] {
return 123; // Error
~~~~~~~~~~~
!!! error TS2322: Type '123' is not assignable to type 'Type[K]'.
!!! error TS2322: Type '123' is not assignable to type '123 & "some string"'.
!!! error TS2322: Type '123' is not assignable to type '"some string"'.
}

Loading