From ad6fcb4e7f2006013087116ee9d85a04adf7c9cd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 21 Jan 2024 17:18:19 -0800 Subject: [PATCH 1/8] Improve getResolvedApparentTypeOfMappedType --- src/compiler/checker.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd24f9be315ee..2794ce492f84f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14542,11 +14542,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getResolvedApparentTypeOfMappedType(type: MappedType) { - const typeVariable = getHomomorphicTypeVariable(type); - if (typeVariable && !type.declaration.nameType) { - const constraint = getConstraintOfTypeParameter(typeVariable); - if (constraint && everyType(constraint, isArrayOrTupleType)) { - return instantiateType(type, prependTypeMapping(typeVariable, constraint, type.mapper)); + const target = (type.target ?? type) as MappedType; + const typeVariable = getHomomorphicTypeVariable(target); + if (typeVariable && !target.declaration.nameType) { + const constraint = getConstraintTypeFromMappedType(type); + if (constraint.flags & TypeFlags.Index) { + const baseConstraint = getBaseConstraintOfType((constraint as IndexType).type); + if (baseConstraint && everyType(baseConstraint, isArrayOrTupleType)) { + return instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, type.mapper)); + } } } return type; From f5110fe946daa24ff6e6bab50b163b24f3571148 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 21 Jan 2024 17:22:22 -0800 Subject: [PATCH 2/8] Add regression test --- ...omomorphicInstantiationSpreadable1.symbols | 93 ++++++++++++++++++ ...nHomomorphicInstantiationSpreadable1.types | 96 +++++++++++++++++++ ...hNonHomomorphicInstantiationSpreadable1.ts | 42 ++++++++ 3 files changed, 231 insertions(+) create mode 100644 tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols create mode 100644 tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types create mode 100644 tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts diff --git a/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols new file mode 100644 index 0000000000000..f643150cf97a1 --- /dev/null +++ b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.symbols @@ -0,0 +1,93 @@ +//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] //// + +=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts === +type HandleOptions = { +>HandleOptions : Symbol(HandleOptions, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 0)) +>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19)) + + [I in keyof O]: { +>I : Symbol(I, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 5)) +>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19)) + + value: O[I]; +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 21)) +>O : Symbol(O, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 19)) +>I : Symbol(I, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 1, 5)) + + }; +}; + +declare function func1< +>func1 : Symbol(func1, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 4, 2)) + + T extends Record, +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) + +>(fields: { +>fields : Symbol(fields, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 8, 2)) + + [K in keyof T]: { +>K : Symbol(K, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 5)) +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) + + label: string; +>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 21)) + + options: [...HandleOptions]; +>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 10, 22)) +>HandleOptions : Symbol(HandleOptions, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 0, 0)) +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) +>K : Symbol(K, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 9, 5)) + + }; +}): T; +>T : Symbol(T, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 6, 23)) + +const result = func1({ +>result : Symbol(result, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 15, 5)) +>func1 : Symbol(func1, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 4, 2)) + + prop: { +>prop : Symbol(prop, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 15, 22)) + + label: "first", +>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 16, 11)) + + options: [ +>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 17, 23)) + { + value: 123, +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 19, 13)) + + }, + { + value: "foo", +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 22, 13)) + + }, + ], + }, + other: { +>other : Symbol(other, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 26, 6)) + + label: "second", +>label : Symbol(label, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 27, 12)) + + options: [ +>options : Symbol(options, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 28, 24)) + { + value: "bar", +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 30, 13)) + + }, + { + value: true, +>value : Symbol(value, Decl(homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts, 33, 13)) + + }, + ], + }, +}); + diff --git a/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types new file mode 100644 index 0000000000000..d50293e983f5f --- /dev/null +++ b/tests/baselines/reference/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.types @@ -0,0 +1,96 @@ +//// [tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts] //// + +=== homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts === +type HandleOptions = { +>HandleOptions : HandleOptions + + [I in keyof O]: { + value: O[I]; +>value : O[I] + + }; +}; + +declare function func1< +>func1 : >(fields: { [K in keyof T]: { label: string; options: [...HandleOptions]; }; }) => T + + T extends Record, +>(fields: { +>fields : { [K in keyof T]: { label: string; options: [...HandleOptions]; }; } + + [K in keyof T]: { + label: string; +>label : string + + options: [...HandleOptions]; +>options : [...HandleOptions] + + }; +}): T; + +const result = func1({ +>result : { prop: [number, string]; other: [string, boolean]; } +>func1({ prop: { label: "first", options: [ { value: 123, }, { value: "foo", }, ], }, other: { label: "second", options: [ { value: "bar", }, { value: true, }, ], },}) : { prop: [number, string]; other: [string, boolean]; } +>func1 : >(fields: { [K in keyof T]: { label: string; options: [...HandleOptions]; }; }) => T +>{ prop: { label: "first", options: [ { value: 123, }, { value: "foo", }, ], }, other: { label: "second", options: [ { value: "bar", }, { value: true, }, ], },} : { prop: { label: string; options: [{ value: number; }, { value: string; }]; }; other: { label: string; options: [{ value: string; }, { value: true; }]; }; } + + prop: { +>prop : { label: string; options: [{ value: number; }, { value: string; }]; } +>{ label: "first", options: [ { value: 123, }, { value: "foo", }, ], } : { label: string; options: [{ value: number; }, { value: string; }]; } + + label: "first", +>label : string +>"first" : "first" + + options: [ +>options : [{ value: number; }, { value: string; }] +>[ { value: 123, }, { value: "foo", }, ] : [{ value: number; }, { value: string; }] + { +>{ value: 123, } : { value: number; } + + value: 123, +>value : number +>123 : 123 + + }, + { +>{ value: "foo", } : { value: string; } + + value: "foo", +>value : string +>"foo" : "foo" + + }, + ], + }, + other: { +>other : { label: string; options: [{ value: string; }, { value: true; }]; } +>{ label: "second", options: [ { value: "bar", }, { value: true, }, ], } : { label: string; options: [{ value: string; }, { value: true; }]; } + + label: "second", +>label : string +>"second" : "second" + + options: [ +>options : [{ value: string; }, { value: true; }] +>[ { value: "bar", }, { value: true, }, ] : [{ value: string; }, { value: true; }] + { +>{ value: "bar", } : { value: string; } + + value: "bar", +>value : string +>"bar" : "bar" + + }, + { +>{ value: true, } : { value: true; } + + value: true, +>value : true +>true : true + + }, + ], + }, +}); + diff --git a/tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts b/tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts new file mode 100644 index 0000000000000..08990dc86aec7 --- /dev/null +++ b/tests/cases/compiler/homomorphicMappedTypeWithNonHomomorphicInstantiationSpreadable1.ts @@ -0,0 +1,42 @@ +// @strict: true +// @noEmit: true + +type HandleOptions = { + [I in keyof O]: { + value: O[I]; + }; +}; + +declare function func1< + T extends Record, +>(fields: { + [K in keyof T]: { + label: string; + options: [...HandleOptions]; + }; +}): T; + +const result = func1({ + prop: { + label: "first", + options: [ + { + value: 123, + }, + { + value: "foo", + }, + ], + }, + other: { + label: "second", + options: [ + { + value: "bar", + }, + { + value: true, + }, + ], + }, +}); From 14724a095617a2044d32023d60ff48771c15425c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 21 Jan 2024 17:27:21 -0800 Subject: [PATCH 3/8] Accept new baselines (break is due to #29919) --- .../reference/ramdaToolsNoInfinite.errors.txt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 tests/baselines/reference/ramdaToolsNoInfinite.errors.txt diff --git a/tests/baselines/reference/ramdaToolsNoInfinite.errors.txt b/tests/baselines/reference/ramdaToolsNoInfinite.errors.txt new file mode 100644 index 0000000000000..40e9e8ca6655a --- /dev/null +++ b/tests/baselines/reference/ramdaToolsNoInfinite.errors.txt @@ -0,0 +1,126 @@ +ramdaToolsNoInfinite.ts(115,27): error TS2370: A rest parameter must be of an array type. + + +==== ramdaToolsNoInfinite.ts (1 errors) ==== + // All the following types are explained here: + // https://medium.freecodecamp.org/typescript-curry-ramda-types-f747e99744ab + // https://github.com/pirix-gh/medium/blob/master/types-curry-ramda/src/index.ts + declare namespace Tools { + type Head = + T extends [any, ...any[]] + ? T[0] + : never; + + type Tail = + ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) + ? TT + : []; + + type HasTail = + T extends ([] | [any]) + ? false + : true; + + type Last = { + 0: Last>; + 1: Head; + }[ + HasTail extends true + ? 0 + : 1 + ]; + + type Length = + T['length']; + + type Prepend = + ((head: E, ...args: T) => any) extends ((...args: infer U) => any) + ? U + : T; + + type Drop = { + 0: Drop, Prepend>; + 1: T; + }[ + Length extends N + ? 1 + : 0 + ]; + + type Cast = X extends Y ? X : Y; + + type Pos = + Length; + + type Next = + Prepend; + + type Prev = + Tail; + + type Iterator = { + 0: Iterator, Next>; + 1: From; + }[ + Pos extends Index + ? 1 + : 0 + ]; + + type Reverse = { + 0: Reverse], R>, Next>; + 1: R; + }[ + Pos extends Length + ? 1 + : 0 + ]; + + type Concat = + Reverse extends infer R ? Cast : never, T2>; + + type Append = + Concat; + + type ValueOfRecord = R extends Record ? T : never; + } + + declare namespace R { + export type Placeholder = { __placeholder: void }; + } + + declare namespace Curry { + type GapOf = + T1[Tools.Pos] extends R.Placeholder + ? Tools.Append], TN> + : TN; + + interface GapsOfWorker { + 0: GapsOf extends infer G ? Tools.Cast : never, Tools.Next>; + 1: Tools.Concat, T2> extends infer D ? Tools.Cast : never>; + } + type GapsOf = GapsOfWorker[ + Tools.Pos extends Tools.Length + ? 1 + : 0 + ]; + + type PartialGaps = { + [K in keyof T]?: T[K] | R.Placeholder + }; + + type CleanedGaps = { + [K in keyof T]: NonNullable + }; + + type Gaps = CleanedGaps>; + + type Curry any)> = + (...args: Tools.Cast>>, any[]>) => + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2370: A rest parameter must be of an array type. + GapsOf> extends [any, ...any[]] + ? Curry<(...args: GapsOf> extends infer G ? Tools.Cast : never) => ReturnType> + : ReturnType; + } + \ No newline at end of file From ee46bdeea136c849d280db357e91c7e1c2bbe76a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 16 Feb 2024 13:51:27 -0800 Subject: [PATCH 4/8] Make any[] the effective rest type when any is spread --- src/compiler/checker.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1972e443c1c35..6f75b80b6ff8d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20837,8 +20837,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const restIndex = sourceRestType || targetRestType ? paramCount - 1 : -1; for (let i = 0; i < paramCount; i++) { - const sourceType = i === restIndex ? getRestTypeAtPosition(source, i) : tryGetTypeAtPosition(source, i); - const targetType = i === restIndex ? getRestTypeAtPosition(target, i) : tryGetTypeAtPosition(target, i); + const sourceType = i === restIndex ? getRestOrAnyTypeAtPosition(source, i) : tryGetTypeAtPosition(source, i); + const targetType = i === restIndex ? getRestOrAnyTypeAtPosition(target, i) : tryGetTypeAtPosition(target, i); if (sourceType && targetType) { // In order to ensure that any generic type Foo is at least co-variant with respect to T no matter // how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions, @@ -36464,6 +36464,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createTupleType(types, flags, readonly, names); } + // Return the rest type at the given position, transforming `any[]` into just `any`. We do this because + // in signatures we want `any[]` in a rest position to be compatible with anything, but `any[]` isn't + // assignable to tuple types with required elements. + function getRestOrAnyTypeAtPosition(source: Signature, pos: number): Type { + const restType = getRestTypeAtPosition(source, pos); + const elementType = restType && getElementTypeOfArrayType(restType); + return elementType && isTypeAny(elementType) ? anyType : restType; + } + // Return the number of parameters in a signature. The rest parameter, if present, counts as one // parameter. For example, the parameter count of (x: number, y: number, ...z: string[]) is 3 and // the parameter count of (x: number, ...args: [number, ...string[], boolean])) is also 3. In the @@ -36527,7 +36536,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (signatureHasRestParameter(signature)) { const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); if (!isTupleType(restType)) { - return restType; + return isTypeAny(restType) ? anyArrayType : restType; } if (restType.target.hasRestElement) { return sliceTupleType(restType, restType.target.fixedLength); From 1a9d2af08463ded308f17deddb237aa9ea2c0642 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 16 Feb 2024 13:55:12 -0800 Subject: [PATCH 5/8] Accept new baselines --- .../reference/ramdaToolsNoInfinite.errors.txt | 126 ------------------ 1 file changed, 126 deletions(-) delete mode 100644 tests/baselines/reference/ramdaToolsNoInfinite.errors.txt diff --git a/tests/baselines/reference/ramdaToolsNoInfinite.errors.txt b/tests/baselines/reference/ramdaToolsNoInfinite.errors.txt deleted file mode 100644 index 40e9e8ca6655a..0000000000000 --- a/tests/baselines/reference/ramdaToolsNoInfinite.errors.txt +++ /dev/null @@ -1,126 +0,0 @@ -ramdaToolsNoInfinite.ts(115,27): error TS2370: A rest parameter must be of an array type. - - -==== ramdaToolsNoInfinite.ts (1 errors) ==== - // All the following types are explained here: - // https://medium.freecodecamp.org/typescript-curry-ramda-types-f747e99744ab - // https://github.com/pirix-gh/medium/blob/master/types-curry-ramda/src/index.ts - declare namespace Tools { - type Head = - T extends [any, ...any[]] - ? T[0] - : never; - - type Tail = - ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) - ? TT - : []; - - type HasTail = - T extends ([] | [any]) - ? false - : true; - - type Last = { - 0: Last>; - 1: Head; - }[ - HasTail extends true - ? 0 - : 1 - ]; - - type Length = - T['length']; - - type Prepend = - ((head: E, ...args: T) => any) extends ((...args: infer U) => any) - ? U - : T; - - type Drop = { - 0: Drop, Prepend>; - 1: T; - }[ - Length extends N - ? 1 - : 0 - ]; - - type Cast = X extends Y ? X : Y; - - type Pos = - Length; - - type Next = - Prepend; - - type Prev = - Tail; - - type Iterator = { - 0: Iterator, Next>; - 1: From; - }[ - Pos extends Index - ? 1 - : 0 - ]; - - type Reverse = { - 0: Reverse], R>, Next>; - 1: R; - }[ - Pos extends Length - ? 1 - : 0 - ]; - - type Concat = - Reverse extends infer R ? Cast : never, T2>; - - type Append = - Concat; - - type ValueOfRecord = R extends Record ? T : never; - } - - declare namespace R { - export type Placeholder = { __placeholder: void }; - } - - declare namespace Curry { - type GapOf = - T1[Tools.Pos] extends R.Placeholder - ? Tools.Append], TN> - : TN; - - interface GapsOfWorker { - 0: GapsOf extends infer G ? Tools.Cast : never, Tools.Next>; - 1: Tools.Concat, T2> extends infer D ? Tools.Cast : never>; - } - type GapsOf = GapsOfWorker[ - Tools.Pos extends Tools.Length - ? 1 - : 0 - ]; - - type PartialGaps = { - [K in keyof T]?: T[K] | R.Placeholder - }; - - type CleanedGaps = { - [K in keyof T]: NonNullable - }; - - type Gaps = CleanedGaps>; - - type Curry any)> = - (...args: Tools.Cast>>, any[]>) => - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2370: A rest parameter must be of an array type. - GapsOf> extends [any, ...any[]] - ? Curry<(...args: GapsOf> extends infer G ? Tools.Cast : never) => ReturnType> - : ReturnType; - } - \ No newline at end of file From cd93df134d760f8a96b90095e766f615411c38b6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 16 Feb 2024 14:23:12 -0800 Subject: [PATCH 6/8] Additional test --- ...assignmentToAnyArrayRestParameters.symbols | 42 +++++++++++++++++++ .../assignmentToAnyArrayRestParameters.types | 40 ++++++++++++++++++ .../assignmentToAnyArrayRestParameters.ts | 14 +++++++ 3 files changed, 96 insertions(+) create mode 100644 tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols create mode 100644 tests/baselines/reference/assignmentToAnyArrayRestParameters.types create mode 100644 tests/cases/compiler/assignmentToAnyArrayRestParameters.ts diff --git a/tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols b/tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols new file mode 100644 index 0000000000000..2491c23fd0b17 --- /dev/null +++ b/tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/assignmentToAnyArrayRestParameters.ts] //// + +=== assignmentToAnyArrayRestParameters.ts === +// Repro from #57122 + +function foo( +>foo : Symbol(foo, Decl(assignmentToAnyArrayRestParameters.ts, 0, 0)) +>T : Symbol(T, Decl(assignmentToAnyArrayRestParameters.ts, 2, 13)) + + fa: (s: string, ...args: string[]) => string, +>fa : Symbol(fa, Decl(assignmentToAnyArrayRestParameters.ts, 2, 33)) +>s : Symbol(s, Decl(assignmentToAnyArrayRestParameters.ts, 3, 9)) +>args : Symbol(args, Decl(assignmentToAnyArrayRestParameters.ts, 3, 19)) + + fb: (s: string, ...args: T) => string +>fb : Symbol(fb, Decl(assignmentToAnyArrayRestParameters.ts, 3, 49)) +>s : Symbol(s, Decl(assignmentToAnyArrayRestParameters.ts, 4, 9)) +>args : Symbol(args, Decl(assignmentToAnyArrayRestParameters.ts, 4, 19)) +>T : Symbol(T, Decl(assignmentToAnyArrayRestParameters.ts, 2, 13)) + +) { + const f1: (...args: any) => string = fa; +>f1 : Symbol(f1, Decl(assignmentToAnyArrayRestParameters.ts, 6, 9)) +>args : Symbol(args, Decl(assignmentToAnyArrayRestParameters.ts, 6, 15)) +>fa : Symbol(fa, Decl(assignmentToAnyArrayRestParameters.ts, 2, 33)) + + const f2: (...args: any[]) => string = fa; +>f2 : Symbol(f2, Decl(assignmentToAnyArrayRestParameters.ts, 7, 9)) +>args : Symbol(args, Decl(assignmentToAnyArrayRestParameters.ts, 7, 15)) +>fa : Symbol(fa, Decl(assignmentToAnyArrayRestParameters.ts, 2, 33)) + + const f3: (...args: any) => string = fb; +>f3 : Symbol(f3, Decl(assignmentToAnyArrayRestParameters.ts, 8, 9)) +>args : Symbol(args, Decl(assignmentToAnyArrayRestParameters.ts, 8, 15)) +>fb : Symbol(fb, Decl(assignmentToAnyArrayRestParameters.ts, 3, 49)) + + const f4: (...args: any[]) => string = fb; +>f4 : Symbol(f4, Decl(assignmentToAnyArrayRestParameters.ts, 9, 9)) +>args : Symbol(args, Decl(assignmentToAnyArrayRestParameters.ts, 9, 15)) +>fb : Symbol(fb, Decl(assignmentToAnyArrayRestParameters.ts, 3, 49)) +} + diff --git a/tests/baselines/reference/assignmentToAnyArrayRestParameters.types b/tests/baselines/reference/assignmentToAnyArrayRestParameters.types new file mode 100644 index 0000000000000..db2bc752b4a75 --- /dev/null +++ b/tests/baselines/reference/assignmentToAnyArrayRestParameters.types @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/assignmentToAnyArrayRestParameters.ts] //// + +=== assignmentToAnyArrayRestParameters.ts === +// Repro from #57122 + +function foo( +>foo : (fa: (s: string, ...args: string[]) => string, fb: (s: string, ...args: T) => string) => void + + fa: (s: string, ...args: string[]) => string, +>fa : (s: string, ...args: string[]) => string +>s : string +>args : string[] + + fb: (s: string, ...args: T) => string +>fb : (s: string, ...args: T) => string +>s : string +>args : T + +) { + const f1: (...args: any) => string = fa; +>f1 : (...args: any) => string +>args : any +>fa : (s: string, ...args: string[]) => string + + const f2: (...args: any[]) => string = fa; +>f2 : (...args: any[]) => string +>args : any[] +>fa : (s: string, ...args: string[]) => string + + const f3: (...args: any) => string = fb; +>f3 : (...args: any) => string +>args : any +>fb : (s: string, ...args: T) => string + + const f4: (...args: any[]) => string = fb; +>f4 : (...args: any[]) => string +>args : any[] +>fb : (s: string, ...args: T) => string +} + diff --git a/tests/cases/compiler/assignmentToAnyArrayRestParameters.ts b/tests/cases/compiler/assignmentToAnyArrayRestParameters.ts new file mode 100644 index 0000000000000..51a5d4317a858 --- /dev/null +++ b/tests/cases/compiler/assignmentToAnyArrayRestParameters.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +// Repro from #57122 + +function foo( + fa: (s: string, ...args: string[]) => string, + fb: (s: string, ...args: T) => string +) { + const f1: (...args: any) => string = fa; + const f2: (...args: any[]) => string = fa; + const f3: (...args: any) => string = fb; + const f4: (...args: any[]) => string = fb; +} From 3941375ffb26a6c86d6fe5b18f0852df6897a462 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 18 Feb 2024 08:46:47 -1000 Subject: [PATCH 7/8] Consistent indexing with round-tripping numeric string literals --- src/compiler/checker.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6f75b80b6ff8d..a92962dd9f046 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -40536,7 +40536,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const objectIndexType = isGenericMappedType(objectType) && getMappedTypeNameTypeKind(objectType) === MappedTypeNameTypeKind.Remapping ? getIndexTypeForMappedType(objectType, IndexFlags.None) : getIndexType(objectType, IndexFlags.None); - if (isTypeAssignableTo(indexType, objectIndexType)) { + const hasNumberIndexInfo = !!getIndexInfoOfType(objectType, numberType); + if (everyType(indexType, t => isTypeAssignableTo(t, objectIndexType) || hasNumberIndexInfo && isApplicableIndexType(t, numberType))) { if ( accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(objectType as MappedType) & MappedTypeModifiers.IncludeReadonly @@ -40545,16 +40546,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return type; } - // Check if we're indexing with a numeric type and if either object or index types - // is a generic type with a constraint that has a numeric index signature. - const apparentObjectType = getApparentType(objectType); - if (getIndexInfoOfType(apparentObjectType, numberType) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { - return type; - } if (isGenericObjectType(objectType)) { const propertyName = getPropertyNameFromIndex(indexType, accessNode); if (propertyName) { - const propertySymbol = forEachType(apparentObjectType, t => getPropertyOfType(t, propertyName)); + const propertySymbol = forEachType(getApparentType(objectType), t => getPropertyOfType(t, propertyName)); if (propertySymbol && getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.NonPublicAccessibilityModifier) { error(accessNode, Diagnostics.Private_or_protected_member_0_cannot_be_accessed_on_a_type_parameter, unescapeLeadingUnderscores(propertyName)); return errorType; From 633bfc32b33109e22a4efa988869148d4acee164 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 18 Feb 2024 08:46:58 -1000 Subject: [PATCH 8/8] Add tests --- ...ignmentToAnyArrayRestParameters.errors.txt | 30 ++++++++++++++++++ ...assignmentToAnyArrayRestParameters.symbols | 31 ++++++++++++++++++- .../assignmentToAnyArrayRestParameters.types | 24 +++++++++++++- .../assignmentToAnyArrayRestParameters.ts | 11 ++++++- 4 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/assignmentToAnyArrayRestParameters.errors.txt diff --git a/tests/baselines/reference/assignmentToAnyArrayRestParameters.errors.txt b/tests/baselines/reference/assignmentToAnyArrayRestParameters.errors.txt new file mode 100644 index 0000000000000..5644d888d42b9 --- /dev/null +++ b/tests/baselines/reference/assignmentToAnyArrayRestParameters.errors.txt @@ -0,0 +1,30 @@ +assignmentToAnyArrayRestParameters.ts(15,25): error TS2339: Property '0.0' does not exist on type 'string[]'. +assignmentToAnyArrayRestParameters.ts(18,16): error TS2536: Type '"0.0"' cannot be used to index type 'T'. + + +==== assignmentToAnyArrayRestParameters.ts (2 errors) ==== + // Repros from #57122 + + function foo( + fa: (s: string, ...args: string[]) => string, + fb: (s: string, ...args: T) => string + ) { + const f1: (...args: any) => string = fa; + const f2: (...args: any[]) => string = fa; + const f3: (...args: any) => string = fb; + const f4: (...args: any[]) => string = fb; + } + + function bar() { + type T00 = string[]["0"]; + type T01 = string[]["0.0"]; // Error + ~~~~~ +!!! error TS2339: Property '0.0' does not exist on type 'string[]'. + type T02 = string[][K | "0"]; + type T10 = T["0"]; + type T11 = T["0.0"]; // Error + ~~~~~~~~ +!!! error TS2536: Type '"0.0"' cannot be used to index type 'T'. + type T12 = T[K | "0"]; + } + \ No newline at end of file diff --git a/tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols b/tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols index 2491c23fd0b17..a5c7496cdb8b7 100644 --- a/tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols +++ b/tests/baselines/reference/assignmentToAnyArrayRestParameters.symbols @@ -1,7 +1,7 @@ //// [tests/cases/compiler/assignmentToAnyArrayRestParameters.ts] //// === assignmentToAnyArrayRestParameters.ts === -// Repro from #57122 +// Repros from #57122 function foo( >foo : Symbol(foo, Decl(assignmentToAnyArrayRestParameters.ts, 0, 0)) @@ -40,3 +40,32 @@ function foo( >fb : Symbol(fb, Decl(assignmentToAnyArrayRestParameters.ts, 3, 49)) } +function bar() { +>bar : Symbol(bar, Decl(assignmentToAnyArrayRestParameters.ts, 10, 1)) +>T : Symbol(T, Decl(assignmentToAnyArrayRestParameters.ts, 12, 13)) +>K : Symbol(K, Decl(assignmentToAnyArrayRestParameters.ts, 12, 32)) + + type T00 = string[]["0"]; +>T00 : Symbol(T00, Decl(assignmentToAnyArrayRestParameters.ts, 12, 54)) + + type T01 = string[]["0.0"]; // Error +>T01 : Symbol(T01, Decl(assignmentToAnyArrayRestParameters.ts, 13, 29)) + + type T02 = string[][K | "0"]; +>T02 : Symbol(T02, Decl(assignmentToAnyArrayRestParameters.ts, 14, 31)) +>K : Symbol(K, Decl(assignmentToAnyArrayRestParameters.ts, 12, 32)) + + type T10 = T["0"]; +>T10 : Symbol(T10, Decl(assignmentToAnyArrayRestParameters.ts, 15, 33)) +>T : Symbol(T, Decl(assignmentToAnyArrayRestParameters.ts, 12, 13)) + + type T11 = T["0.0"]; // Error +>T11 : Symbol(T11, Decl(assignmentToAnyArrayRestParameters.ts, 16, 22)) +>T : Symbol(T, Decl(assignmentToAnyArrayRestParameters.ts, 12, 13)) + + type T12 = T[K | "0"]; +>T12 : Symbol(T12, Decl(assignmentToAnyArrayRestParameters.ts, 17, 24)) +>T : Symbol(T, Decl(assignmentToAnyArrayRestParameters.ts, 12, 13)) +>K : Symbol(K, Decl(assignmentToAnyArrayRestParameters.ts, 12, 32)) +} + diff --git a/tests/baselines/reference/assignmentToAnyArrayRestParameters.types b/tests/baselines/reference/assignmentToAnyArrayRestParameters.types index db2bc752b4a75..c4bb45b3c662e 100644 --- a/tests/baselines/reference/assignmentToAnyArrayRestParameters.types +++ b/tests/baselines/reference/assignmentToAnyArrayRestParameters.types @@ -1,7 +1,7 @@ //// [tests/cases/compiler/assignmentToAnyArrayRestParameters.ts] //// === assignmentToAnyArrayRestParameters.ts === -// Repro from #57122 +// Repros from #57122 function foo( >foo : (fa: (s: string, ...args: string[]) => string, fb: (s: string, ...args: T) => string) => void @@ -38,3 +38,25 @@ function foo( >fb : (s: string, ...args: T) => string } +function bar() { +>bar : () => void + + type T00 = string[]["0"]; +>T00 : string + + type T01 = string[]["0.0"]; // Error +>T01 : any + + type T02 = string[][K | "0"]; +>T02 : string[][K | "0"] + + type T10 = T["0"]; +>T10 : T["0"] + + type T11 = T["0.0"]; // Error +>T11 : T["0.0"] + + type T12 = T[K | "0"]; +>T12 : T[K | "0"] +} + diff --git a/tests/cases/compiler/assignmentToAnyArrayRestParameters.ts b/tests/cases/compiler/assignmentToAnyArrayRestParameters.ts index 51a5d4317a858..97b665da22d3c 100644 --- a/tests/cases/compiler/assignmentToAnyArrayRestParameters.ts +++ b/tests/cases/compiler/assignmentToAnyArrayRestParameters.ts @@ -1,7 +1,7 @@ // @strict: true // @noEmit: true -// Repro from #57122 +// Repros from #57122 function foo( fa: (s: string, ...args: string[]) => string, @@ -12,3 +12,12 @@ function foo( const f3: (...args: any) => string = fb; const f4: (...args: any[]) => string = fb; } + +function bar() { + type T00 = string[]["0"]; + type T01 = string[]["0.0"]; // Error + type T02 = string[][K | "0"]; + type T10 = T["0"]; + type T11 = T["0.0"]; // Error + type T12 = T[K | "0"]; +}