Skip to content

Improve handling of circular constraints #13338

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

Merged
merged 7 commits into from
Jan 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
176 changes: 101 additions & 75 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2803,7 +2803,7 @@ namespace ts {
EnumLike = Enum | EnumLiteral,
UnionOrIntersection = Union | Intersection,
StructuredType = Object | Union | Intersection,
StructuredOrTypeParameter = StructuredType | TypeParameter | Index,
StructuredOrTypeVariable = StructuredType | TypeParameter | Index | IndexedAccess,
TypeVariable = TypeParameter | IndexedAccess,

// 'Narrowable' types are types where narrowing actually narrows.
Expand Down
40 changes: 22 additions & 18 deletions tests/baselines/reference/circularIndexedAccessErrors.errors.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(3,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(7,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(15,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(19,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(23,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(27,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(28,5): error TS2502: 'y' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(29,5): error TS2502: 'z' is referenced directly or indirectly in its own type annotation.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(38,24): error TS2313: Type parameter 'T' has a circular constraint.
tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(38,30): error TS2536: Type '"hello"' cannot be used to index type 'T'.


==== tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts (8 errors) ====
==== tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts (6 errors) ====

type T1 = {
x: T1["x"]; // Error
Expand All @@ -27,9 +25,7 @@ tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(29,5): error
let x2x = x2.x;

interface T3<T extends T3<T>> {
x: T["x"]; // Error
~~~~~~~~~~
!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
x: T["x"];
}

interface T4<T extends T4<T>> {
Expand All @@ -45,13 +41,21 @@ tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(29,5): error
}

class C2 {
x: this["y"]; // Error
~~~~~~~~~~~~~
!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation.
y: this["z"]; // Error
~~~~~~~~~~~~~
!!! error TS2502: 'y' is referenced directly or indirectly in its own type annotation.
z: this["x"]; // Error
~~~~~~~~~~~~~
!!! error TS2502: 'z' is referenced directly or indirectly in its own type annotation.
}
x: this["y"];
y: this["z"];
z: this["x"];
}

// Repro from #12627

interface Foo {
hello: boolean;
}

function foo<T extends Foo | T["hello"]>() {
~~~~~~~~~~~~~~~~
!!! error TS2313: Type parameter 'T' has a circular constraint.
~~~~~~~~~~
!!! error TS2536: Type '"hello"' cannot be used to index type 'T'.
}

26 changes: 21 additions & 5 deletions tests/baselines/reference/circularIndexedAccessErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ declare let x2: T2<"x">;
let x2x = x2.x;

interface T3<T extends T3<T>> {
x: T["x"]; // Error
x: T["x"];
}

interface T4<T extends T4<T>> {
Expand All @@ -25,10 +25,20 @@ class C1 {
}

class C2 {
x: this["y"]; // Error
y: this["z"]; // Error
z: this["x"]; // Error
}
x: this["y"];
y: this["z"];
z: this["x"];
}

// Repro from #12627

interface Foo {
hello: boolean;
}

function foo<T extends Foo | T["hello"]>() {
}


//// [circularIndexedAccessErrors.js]
var x2x = x2.x;
Expand All @@ -42,6 +52,8 @@ var C2 = (function () {
}
return C2;
}());
function foo() {
}


//// [circularIndexedAccessErrors.d.ts]
Expand All @@ -68,3 +80,7 @@ declare class C2 {
y: this["z"];
z: this["x"];
}
interface Foo {
hello: boolean;
}
declare function foo<T extends Foo | T["hello"]>(): void;
119 changes: 118 additions & 1 deletion tests/baselines/reference/keyofAndIndexedAccess.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,30 @@ class C1 {
}
}

type S2 = {
a: string;
b: string;
};

function f90<T extends S2, K extends keyof S2>(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K], x4: T[K]) {
x1 = x2;
x1 = x3;
x1 = x4;
x2 = x1;
x2 = x3;
x2 = x4;
x3 = x1;
x3 = x2;
x3 = x4;
x4 = x1;
x4 = x2;
x4 = x3;
x1.length;
x2.length;
x3.length;
x4.length;
}

// Repros from #12011

class Base {
Expand Down Expand Up @@ -429,7 +453,44 @@ type SomeMethodDescriptor = {
returnValue: string[];
}

let result = dispatchMethod<SomeMethodDescriptor>("someMethod", ["hello", 35]);
let result = dispatchMethod<SomeMethodDescriptor>("someMethod", ["hello", 35]);

// Repro from #13073

type KeyTypes = "a" | "b"
let MyThingy: { [key in KeyTypes]: string[] };

function addToMyThingy<S extends KeyTypes>(key: S) {
MyThingy[key].push("a");
}

// Repro from #13285

function updateIds<T extends Record<K, string>, K extends string>(
obj: T,
idFields: K[],
idMapping: { [oldId: string]: string }
): Record<K, string> {
for (const idField of idFields) {
const newId = idMapping[obj[idField]];
if (newId) {
obj[idField] = newId;
}
}
return obj;
}

// Repro from #13285

function updateIds2<T extends { [x: string]: string }, K extends keyof T>(
obj: T,
key: K,
stringMap: { [oldId: string]: string }
) {
var x = obj[key];
stringMap[x]; // Should be OK.
}


//// [keyofAndIndexedAccess.js]
var __extends = (this && this.__extends) || function (d, b) {
Expand Down Expand Up @@ -644,6 +705,24 @@ var C1 = (function () {
};
return C1;
}());
function f90(x1, x2, x3, x4) {
x1 = x2;
x1 = x3;
x1 = x4;
x2 = x1;
x2 = x3;
x2 = x4;
x3 = x1;
x3 = x2;
x3 = x4;
x4 = x1;
x4 = x2;
x4 = x3;
x1.length;
x2.length;
x3.length;
x4.length;
}
// Repros from #12011
var Base = (function () {
function Base() {
Expand Down Expand Up @@ -713,6 +792,26 @@ function f(p) {
a[p].add; // any
}
var result = dispatchMethod("someMethod", ["hello", 35]);
var MyThingy;
function addToMyThingy(key) {
MyThingy[key].push("a");
}
// Repro from #13285
function updateIds(obj, idFields, idMapping) {
for (var _i = 0, idFields_1 = idFields; _i < idFields_1.length; _i++) {
var idField = idFields_1[_i];
var newId = idMapping[obj[idField]];
if (newId) {
obj[idField] = newId;
}
}
return obj;
}
// Repro from #13285
function updateIds2(obj, key, stringMap) {
var x = obj[key];
stringMap[x]; // Should be OK.
}


//// [keyofAndIndexedAccess.d.ts]
Expand Down Expand Up @@ -848,6 +947,11 @@ declare class C1 {
set<K extends keyof this>(key: K, value: this[K]): void;
foo(): void;
}
declare type S2 = {
a: string;
b: string;
};
declare function f90<T extends S2, K extends keyof S2>(x1: S2[keyof S2], x2: T[keyof S2], x3: S2[K], x4: T[K]): void;
declare class Base {
get<K extends keyof this>(prop: K): this[K];
set<K extends keyof this>(prop: K, value: this[K]): void;
Expand Down Expand Up @@ -920,3 +1024,16 @@ declare type SomeMethodDescriptor = {
returnValue: string[];
};
declare let result: string[];
declare type KeyTypes = "a" | "b";
declare let MyThingy: {
[key in KeyTypes]: string[];
};
declare function addToMyThingy<S extends KeyTypes>(key: S): void;
declare function updateIds<T extends Record<K, string>, K extends string>(obj: T, idFields: K[], idMapping: {
[oldId: string]: string;
}): Record<K, string>;
declare function updateIds2<T extends {
[x: string]: string;
}, K extends keyof T>(obj: T, key: K, stringMap: {
[oldId: string]: string;
}): void;
Loading