Skip to content

Print unions resulting from keyof operations with keyof if possible #20838

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
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
112 changes: 81 additions & 31 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3490,6 +3490,7 @@ namespace ts {
/* @internal */
JsxAttributes = 1 << 26, // Jsx attributes type
MarkerType = 1 << 27, // Marker type used for variance probing
Alias = 1 << 28, // Placeholder type used to point at another type

/* @internal */
Nullable = Undefined | Null,
Expand Down Expand Up @@ -3534,8 +3535,12 @@ namespace ts {
/* @internal */ checker: TypeChecker;
symbol?: Symbol; // Symbol associated with type (if any)
pattern?: DestructuringPattern; // Destructuring pattern represented by type (if any)
aliasSymbol?: Symbol; // Alias associated with type
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
alternativeRepresentation?: Type; // Alternative representation of the type, prefered when printing; not to be used for comparisons or equality
}

export interface AliasType extends Type {
symbol: Symbol;
typeArguments?: Type[];
}

/* @internal */
Expand Down
8 changes: 6 additions & 2 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2046,6 +2046,7 @@ declare namespace ts {
IndexedAccess = 1048576,
NonPrimitive = 33554432,
MarkerType = 134217728,
Alias = 268435456,
Literal = 224,
Unit = 13536,
StringOrNumberLiteral = 96,
Expand All @@ -2067,8 +2068,11 @@ declare namespace ts {
flags: TypeFlags;
symbol?: Symbol;
pattern?: DestructuringPattern;
aliasSymbol?: Symbol;
aliasTypeArguments?: Type[];
alternativeRepresentation?: Type;
}
interface AliasType extends Type {
symbol: Symbol;
typeArguments?: Type[];
}
interface LiteralType extends Type {
value: string | number;
Expand Down
8 changes: 6 additions & 2 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2046,6 +2046,7 @@ declare namespace ts {
IndexedAccess = 1048576,
NonPrimitive = 33554432,
MarkerType = 134217728,
Alias = 268435456,
Literal = 224,
Unit = 13536,
StringOrNumberLiteral = 96,
Expand All @@ -2067,8 +2068,11 @@ declare namespace ts {
flags: TypeFlags;
symbol?: Symbol;
pattern?: DestructuringPattern;
aliasSymbol?: Symbol;
aliasTypeArguments?: Type[];
alternativeRepresentation?: Type;
}
interface AliasType extends Type {
symbol: Symbol;
typeArguments?: Type[];
}
interface LiteralType extends Type {
value: string | number;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//// [declarationUsesKeyofAliasWherePossible.ts]
var Foo = class Foo {
method<T extends keyof ElementTagNameMap>() {};
}

class Bar {
method<T extends keyof ElementTagNameMap>() {};
}

//// [declarationUsesKeyofAliasWherePossible.js]
var Foo = /** @class */ (function () {
function Foo() {
}
Foo.prototype.method = function () { };
;
return Foo;
}());
var Bar = /** @class */ (function () {
function Bar() {
}
Bar.prototype.method = function () { };
;
return Bar;
}());


//// [declarationUsesKeyofAliasWherePossible.d.ts]
declare var Foo: {
new (): {
method<T extends keyof ElementTagNameMap>(): void;
};
};
declare class Bar {
method<T extends keyof ElementTagNameMap>(): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
=== tests/cases/compiler/declarationUsesKeyofAliasWherePossible.ts ===
var Foo = class Foo {
>Foo : Symbol(Foo, Decl(declarationUsesKeyofAliasWherePossible.ts, 0, 3))
>Foo : Symbol(Foo, Decl(declarationUsesKeyofAliasWherePossible.ts, 0, 9))

method<T extends keyof ElementTagNameMap>() {};
>method : Symbol(Foo.method, Decl(declarationUsesKeyofAliasWherePossible.ts, 0, 21))
>T : Symbol(T, Decl(declarationUsesKeyofAliasWherePossible.ts, 1, 11))
>ElementTagNameMap : Symbol(ElementTagNameMap, Decl(lib.dom.d.ts, --, --))
}

class Bar {
>Bar : Symbol(Bar, Decl(declarationUsesKeyofAliasWherePossible.ts, 2, 1))

method<T extends keyof ElementTagNameMap>() {};
>method : Symbol(Bar.method, Decl(declarationUsesKeyofAliasWherePossible.ts, 4, 11))
>T : Symbol(T, Decl(declarationUsesKeyofAliasWherePossible.ts, 5, 11))
>ElementTagNameMap : Symbol(ElementTagNameMap, Decl(lib.dom.d.ts, --, --))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
=== tests/cases/compiler/declarationUsesKeyofAliasWherePossible.ts ===
var Foo = class Foo {
>Foo : typeof Foo
>class Foo { method<T extends keyof ElementTagNameMap>() {};} : typeof Foo
>Foo : typeof Foo

method<T extends keyof ElementTagNameMap>() {};
>method : <T extends keyof ElementTagNameMap>() => void
>T : T
>ElementTagNameMap : ElementTagNameMap
}

class Bar {
>Bar : Bar

method<T extends keyof ElementTagNameMap>() {};
>method : <T extends keyof ElementTagNameMap>() => void
>T : T
>ElementTagNameMap : ElementTagNameMap
}
6 changes: 3 additions & 3 deletions tests/baselines/reference/doubleUnderscoreMappedTypes.types
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ const ok: Properties = {

// As expected, "__property2" is indeed a key of the type
type Keys = keyof Properties;
>Keys : "property1" | "__property2"
>Keys : keyof Properties
>Properties : Properties

const k: Keys = "__property2"; // ok
>k : "property1" | "__property2"
>Keys : "property1" | "__property2"
>k : keyof Properties
>Keys : keyof Properties
>"__property2" : "__property2"

// This should be valid
Expand Down
19 changes: 19 additions & 0 deletions tests/baselines/reference/indirectTypeParameterReferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,22 @@ combined(function (comb) {
comb.a;
});
var n = f(2).a;


//// [indirectTypeParameterReferences.d.ts]
declare type B = {
b: string;
};
declare const flowtypes: <A>(b: B) => {
combined: (fn: (combined: A & B) => void) => any;
literal: (fn: (aPlusB: A & B) => void) => any;
};
declare const combined: (fn: (combined: {
a: string;
} & B) => void) => any, literal: (fn: (aPlusB: {
a: string;
} & B) => void) => any;
declare function f<T>(a: T): {
a: typeof a;
};
declare let n: number;
34 changes: 17 additions & 17 deletions tests/baselines/reference/keyofAndIndexedAccess.types
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ type K07 = keyof never; // never
>K07 : never

type K10 = keyof Shape; // "name" | "width" | "height" | "visible"
>K10 : "name" | "width" | "height" | "visible"
>K10 : keyof Shape
>Shape : Shape

type K11 = keyof Shape[]; // "length" | "toString" | ...
>K11 : "length" | "toString" | "toLocaleString" | "push" | "pop" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | "every" | "some" | "forEach" | "map" | "filter" | "reduce" | "reduceRight"
>K11 : keyof Shape[]
>Shape : Shape

type K12 = keyof Dictionary<Shape>; // string
Expand All @@ -100,7 +100,7 @@ type K13 = keyof {}; // never
>K13 : never

type K14 = keyof Object; // "constructor" | "toString" | ...
>K14 : "toString" | "toLocaleString" | "valueOf" | "constructor" | "hasOwnProperty" | "isPrototypeOf" | "propertyIsEnumerable"
>K14 : keyof Object
>Object : Object

type K15 = keyof E; // "toString" | "toFixed" | "toExponential" | ...
Expand Down Expand Up @@ -131,7 +131,7 @@ type KeyOf<T> = keyof T;
>T : T

type K20 = KeyOf<Shape>; // "name" | "width" | "height" | "visible"
>K20 : "name" | "width" | "height" | "visible"
>K20 : keyof Shape
>KeyOf : keyof T
>Shape : Shape

Expand Down Expand Up @@ -452,17 +452,17 @@ function f20(component: Component<Shape>) {
let name = component.getProperty("name"); // string
>name : string
>component.getProperty("name") : string
>component.getProperty : <K extends "name" | "width" | "height" | "visible">(key: K) => Shape[K]
>component.getProperty : <K extends keyof Shape>(key: K) => Shape[K]
>component : Component<Shape>
>getProperty : <K extends "name" | "width" | "height" | "visible">(key: K) => Shape[K]
>getProperty : <K extends keyof Shape>(key: K) => Shape[K]
>"name" : "name"

let widthOrHeight = component.getProperty(cond ? "width" : "height"); // number
>widthOrHeight : number
>component.getProperty(cond ? "width" : "height") : number
>component.getProperty : <K extends "name" | "width" | "height" | "visible">(key: K) => Shape[K]
>component.getProperty : <K extends keyof Shape>(key: K) => Shape[K]
>component : Component<Shape>
>getProperty : <K extends "name" | "width" | "height" | "visible">(key: K) => Shape[K]
>getProperty : <K extends keyof Shape>(key: K) => Shape[K]
>cond ? "width" : "height" : "width" | "height"
>cond : boolean
>"width" : "width"
Expand All @@ -471,27 +471,27 @@ function f20(component: Component<Shape>) {
let nameOrVisible = component.getProperty(cond ? "name" : "visible"); // string | boolean
>nameOrVisible : string | boolean
>component.getProperty(cond ? "name" : "visible") : string | boolean
>component.getProperty : <K extends "name" | "width" | "height" | "visible">(key: K) => Shape[K]
>component.getProperty : <K extends keyof Shape>(key: K) => Shape[K]
>component : Component<Shape>
>getProperty : <K extends "name" | "width" | "height" | "visible">(key: K) => Shape[K]
>getProperty : <K extends keyof Shape>(key: K) => Shape[K]
>cond ? "name" : "visible" : "name" | "visible"
>cond : boolean
>"name" : "name"
>"visible" : "visible"

component.setProperty("name", "rectangle");
>component.setProperty("name", "rectangle") : void
>component.setProperty : <K extends "name" | "width" | "height" | "visible">(key: K, value: Shape[K]) => void
>component.setProperty : <K extends keyof Shape>(key: K, value: Shape[K]) => void
>component : Component<Shape>
>setProperty : <K extends "name" | "width" | "height" | "visible">(key: K, value: Shape[K]) => void
>setProperty : <K extends keyof Shape>(key: K, value: Shape[K]) => void
>"name" : "name"
>"rectangle" : "rectangle"

component.setProperty(cond ? "width" : "height", 10)
>component.setProperty(cond ? "width" : "height", 10) : void
>component.setProperty : <K extends "name" | "width" | "height" | "visible">(key: K, value: Shape[K]) => void
>component.setProperty : <K extends keyof Shape>(key: K, value: Shape[K]) => void
>component : Component<Shape>
>setProperty : <K extends "name" | "width" | "height" | "visible">(key: K, value: Shape[K]) => void
>setProperty : <K extends keyof Shape>(key: K, value: Shape[K]) => void
>cond ? "width" : "height" : "width" | "height"
>cond : boolean
>"width" : "width"
Expand All @@ -500,9 +500,9 @@ function f20(component: Component<Shape>) {

component.setProperty(cond ? "name" : "visible", true); // Technically not safe
>component.setProperty(cond ? "name" : "visible", true) : void
>component.setProperty : <K extends "name" | "width" | "height" | "visible">(key: K, value: Shape[K]) => void
>component.setProperty : <K extends keyof Shape>(key: K, value: Shape[K]) => void
>component : Component<Shape>
>setProperty : <K extends "name" | "width" | "height" | "visible">(key: K, value: Shape[K]) => void
>setProperty : <K extends keyof Shape>(key: K, value: Shape[K]) => void
>cond ? "name" : "visible" : "name" | "visible"
>cond : boolean
>"name" : "name"
Expand Down Expand Up @@ -563,7 +563,7 @@ function f30(shapes: Shape[]) {
}

function f31<K extends keyof Shape>(key: K) {
>f31 : <K extends "name" | "width" | "height" | "visible">(key: K) => Shape[K]
>f31 : <K extends keyof Shape>(key: K) => Shape[K]
>K : K
>Shape : Shape
>key : K
Expand Down
24 changes: 12 additions & 12 deletions tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(36,21): error
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(41,31): error TS2538: Type 'boolean' cannot be used as an index type.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(46,16): error TS2538: Type 'boolean' cannot be used as an index type.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(49,12): error TS1122: A tuple type element list cannot be empty.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(63,33): error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(64,33): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(66,24): error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(67,24): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(63,33): error TS2345: Argument of type '"size"' is not assignable to parameter of type 'keyof Shape'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(64,33): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type 'keyof Shape'.
Type '"size"' is not assignable to type 'keyof Shape'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(66,24): error TS2345: Argument of type '"size"' is not assignable to parameter of type 'keyof Shape'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(67,24): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type 'keyof Shape'.
Type '"size"' is not assignable to type 'keyof Shape'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(72,5): error TS2536: Type 'keyof (T & U)' cannot be used to index type 'T | U'.
tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(76,5): error TS2322: Type 'T | U' is not assignable to type 'T & U'.
Type 'T' is not assignable to type 'T & U'.
Expand Down Expand Up @@ -132,19 +132,19 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(85,9): error
let x1 = getProperty(shape, "name");
let x2 = getProperty(shape, "size"); // Error
~~~~~~
!!! error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
!!! error TS2345: Argument of type '"size"' is not assignable to parameter of type 'keyof Shape'.
let x3 = getProperty(shape, cond ? "name" : "size"); // Error
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
!!! error TS2345: Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
!!! error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type 'keyof Shape'.
!!! error TS2345: Type '"size"' is not assignable to type 'keyof Shape'.
setProperty(shape, "name", "rectangle");
setProperty(shape, "size", 10); // Error
~~~~~~
!!! error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
!!! error TS2345: Argument of type '"size"' is not assignable to parameter of type 'keyof Shape'.
setProperty(shape, cond ? "name" : "size", 10); // Error
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'.
!!! error TS2345: Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'.
!!! error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type 'keyof Shape'.
!!! error TS2345: Type '"size"' is not assignable to type 'keyof Shape'.
}

function f20<T, U>(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) {
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/keyofAndIndexedAccessErrors.types
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,27 @@ type T00 = keyof K0; // Error
>K0 : No type information available!

type T01 = keyof Object;
>T01 : "toString" | "constructor" | "toLocaleString" | "valueOf" | "hasOwnProperty" | "isPrototypeOf" | "propertyIsEnumerable"
>T01 : keyof Object
>Object : Object

type T02 = keyof keyof Object;
>T02 : "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr"
>T02 : keyof String
>Object : Object

type T03 = keyof keyof keyof Object;
>T03 : "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr"
>T03 : keyof String
>Object : Object

type T04 = keyof keyof keyof keyof Object;
>T04 : "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr"
>T04 : keyof String
>Object : Object

type T05 = keyof keyof keyof keyof keyof Object;
>T05 : "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr"
>T05 : keyof String
>Object : Object

type T06 = keyof keyof keyof keyof keyof keyof Object;
>T06 : "length" | "toString" | "valueOf" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr"
>T06 : keyof String
>Object : Object

type T10 = Shape["name"];
Expand Down
Loading