From d9c56f2f8f1937ad85f65b26f196ef0a41bb887e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Jul 2023 00:58:00 +0200 Subject: [PATCH 01/10] Fixed inferred const rest types in signatures --- src/compiler/checker.ts | 6 +- .../typeParameterConstModifiers.errors.txt | 25 +++++- .../reference/typeParameterConstModifiers.js | 41 +++++++++- .../typeParameterConstModifiers.symbols | 65 ++++++++++++++++ .../typeParameterConstModifiers.types | 76 +++++++++++++++++++ 5 files changed, 208 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 73a15fd578777..23dd5366d1694 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23925,7 +23925,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } if (targetRestType) { - callback(getRestTypeAtPosition(source, paramCount), targetRestType); + callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType)), targetRestType); } } @@ -34928,7 +34928,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } - function getRestTypeAtPosition(source: Signature, pos: number): Type { + function getRestTypeAtPosition(source: Signature, pos: number, readonly?: boolean): Type { const parameterCount = getParameterCount(source); const minArgumentCount = getMinArgumentCount(source); const restType = getEffectiveRestType(source); @@ -34952,7 +34952,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { names.push(name); } } - return createTupleType(types, flags, /*readonly*/ false, length(names) === length(types) ? names : undefined); + return createTupleType(types, flags, readonly, length(names) === length(types) ? names : undefined); } // Return the number of parameters in a signature. The rest parameter, if present, counts as one diff --git a/tests/baselines/reference/typeParameterConstModifiers.errors.txt b/tests/baselines/reference/typeParameterConstModifiers.errors.txt index 8a57e4b939313..872f0fe8910a2 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.errors.txt +++ b/tests/baselines/reference/typeParameterConstModifiers.errors.txt @@ -107,4 +107,27 @@ typeParameterConstModifiers.ts(55,9): error TS1277: 'const' modifier can only ap const thingMapped = >(o: NotEmptyMapped) => o; const tMapped = thingMapped({ foo: '' }); // { foo: "" } - \ No newline at end of file + + // repro from https://github.com/microsoft/TypeScript/issues/55033 + + function factory_55033_minimal(cb: (...args: T) => void) { + return {} as T + } + + const test_55033_minimal = factory_55033_minimal((b: string) => {}) + + function factory_55033(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; + } + + const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" + ); + + const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" + ); \ No newline at end of file diff --git a/tests/baselines/reference/typeParameterConstModifiers.js b/tests/baselines/reference/typeParameterConstModifiers.js index 3fe57ad97a0e4..b52cded2b902d 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.js +++ b/tests/baselines/reference/typeParameterConstModifiers.js @@ -101,7 +101,30 @@ type NotEmptyMapped> = keyof T extends never ? nev const thingMapped = >(o: NotEmptyMapped) => o; const tMapped = thingMapped({ foo: '' }); // { foo: "" } - + +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { + return {} as T +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) + +function factory_55033(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); //// [typeParameterConstModifiers.js] "use strict"; @@ -154,3 +177,19 @@ var thing = function (o) { return o; }; var t = thing({ foo: '' }); // readonly { foo: "" } var thingMapped = function (o) { return o; }; var tMapped = thingMapped({ foo: '' }); // { foo: "" } +// repro from https://github.com/microsoft/TypeScript/issues/55033 +function factory_55033_minimal(cb) { + return {}; +} +var test_55033_minimal = factory_55033_minimal(function (b) { }); +function factory_55033(cb) { + return function call() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return {}; + }; +} +var t1_55033 = factory_55033(function (a, b) { })({ test: 123 }, "some string"); +var t2_55033 = factory_55033(function (a, b) { })({ test: 123 }, "some string"); diff --git a/tests/baselines/reference/typeParameterConstModifiers.symbols b/tests/baselines/reference/typeParameterConstModifiers.symbols index 2ec51bed100f0..371a38201488f 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.symbols +++ b/tests/baselines/reference/typeParameterConstModifiers.symbols @@ -361,3 +361,68 @@ const tMapped = thingMapped({ foo: '' }); // { foo: "" } >thingMapped : Symbol(thingMapped, Decl(typeParameterConstModifiers.ts, 97, 5)) >foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 99, 29)) +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { +>factory_55033_minimal : Symbol(factory_55033_minimal, Decl(typeParameterConstModifiers.ts, 99, 41)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 103, 31)) +>cb : Symbol(cb, Decl(typeParameterConstModifiers.ts, 103, 67)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 103, 72)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 103, 31)) + + return {} as T +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 103, 31)) +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) +>test_55033_minimal : Symbol(test_55033_minimal, Decl(typeParameterConstModifiers.ts, 107, 5)) +>factory_55033_minimal : Symbol(factory_55033_minimal, Decl(typeParameterConstModifiers.ts, 99, 41)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 107, 50)) + +function factory_55033(cb: (...args: T) => void) { +>factory_55033 : Symbol(factory_55033, Decl(typeParameterConstModifiers.ts, 107, 67)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 109, 23)) +>cb : Symbol(cb, Decl(typeParameterConstModifiers.ts, 109, 59)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 109, 64)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 109, 23)) + + return function call(...args: K): K { +>call : Symbol(call, Decl(typeParameterConstModifiers.ts, 110, 10)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 109, 23)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 110, 44)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) + + return {} as K; +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) + + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t1_55033 : Symbol(t1_55033, Decl(typeParameterConstModifiers.ts, 115, 5)) +>factory_55033 : Symbol(factory_55033, Decl(typeParameterConstModifiers.ts, 107, 67)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 115, 32)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 115, 36)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 115, 52)) + + { test: 123 }, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 116, 5)) + + "some string" +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t2_55033 : Symbol(t2_55033, Decl(typeParameterConstModifiers.ts, 120, 5)) +>factory_55033 : Symbol(factory_55033, Decl(typeParameterConstModifiers.ts, 107, 67)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 120, 32)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 120, 36)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 120, 52)) + + { test: 123 } as const, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 121, 5)) +>const : Symbol(const) + + "some string" +); diff --git a/tests/baselines/reference/typeParameterConstModifiers.types b/tests/baselines/reference/typeParameterConstModifiers.types index 809a64ce0b70c..6c87617c96c9a 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.types +++ b/tests/baselines/reference/typeParameterConstModifiers.types @@ -409,3 +409,79 @@ const tMapped = thingMapped({ foo: '' }); // { foo: "" } >foo : "" >'' : "" +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { +>factory_55033_minimal : (cb: (...args: T) => void) => T +>cb : (...args: T) => void +>args : T + + return {} as T +>{} as T : T +>{} : {} +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) +>test_55033_minimal : readonly [b: string] +>factory_55033_minimal((b: string) => {}) : readonly [b: string] +>factory_55033_minimal : (cb: (...args: T) => void) => T +>(b: string) => {} : (b: string) => void +>b : string + +function factory_55033(cb: (...args: T) => void) { +>factory_55033 : (cb: (...args: T) => void) => (...args: K) => K +>cb : (...args: T) => void +>args : T + + return function call(...args: K): K { +>function call(...args: K): K { return {} as K; } : (...args: K) => K +>call : (...args: K) => K +>args : K + + return {} as K; +>{} as K : K +>{} : {} + + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t1_55033 : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {})( { test: 123 }, "some string") : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 }, +>{ test: 123 } : { test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t2_55033 : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {})( { test: 123 } as const, "some string") : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 } as const, +>{ test: 123 } as const : { readonly test: 123; } +>{ test: 123 } : { readonly test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); From fb32b835e3b70399d2cb2c261f782739c41d672a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Jul 2023 08:45:11 +0200 Subject: [PATCH 02/10] Add missing test change --- .../typeParameterConstModifiers.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts index b66b92ff7024b..448f007da972d 100644 --- a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts +++ b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts @@ -100,3 +100,27 @@ type NotEmptyMapped> = keyof T extends never ? nev const thingMapped = >(o: NotEmptyMapped) => o; const tMapped = thingMapped({ foo: '' }); // { foo: "" } + +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { + return {} as T +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) + +function factory_55033(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); \ No newline at end of file From a5e55adc07fe3c5b7923013bb56f6dbbb41e5f3e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 1 Aug 2023 11:22:09 -0700 Subject: [PATCH 03/10] Infer readonly array/tuple for const T only when T's constraint permits --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 23dd5366d1694..27cf21ca83043 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30045,7 +30045,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createTupleType(elementTypes, elementFlags); } if (forceTuple || inConstContext || inTupleContext) { - return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext)); + return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext && !(contextualType && isMutableArrayOrTuple(contextualType)))); } return createArrayLiteralType(createArrayType(elementTypes.length ? getUnionType(sameMap(elementTypes, (t, i) => elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessTypeOrUndefined(t, numberType) || anyType : t), UnionReduction.Subtype) : @@ -32623,7 +32623,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { names.push((arg as SyntheticExpression).tupleNameSource!); } } - return createTupleType(types, flags, inConstContext, length(names) === length(types) ? names : undefined); + return createTupleType(types, flags, inConstContext && !isMutableArrayOrTuple(getBaseConstraintOrType(restType)), length(names) === length(types) ? names : undefined); } function checkTypeArguments(signature: Signature, typeArgumentNodes: readonly TypeNode[], reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined { From 8f07af995cc4bff4c9045b9b3a0f5e8526150911 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 1 Aug 2023 11:58:34 -0700 Subject: [PATCH 04/10] Same rules for inferences made from signatures --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 27cf21ca83043..b8489eeb8fe9d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23925,7 +23925,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } if (targetRestType) { - callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType)), targetRestType); + callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType) && !isMutableArrayOrTuple(getBaseConstraintOrType(targetRestType))), targetRestType); } } From f6833cd4d68d040e31cf3c40c456c982ffec1cf3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 1 Aug 2023 12:14:34 -0700 Subject: [PATCH 05/10] Add more tests --- .../typeParameterConstModifiers.errors.txt | 51 +++++- .../reference/typeParameterConstModifiers.js | 71 +++++++- .../typeParameterConstModifiers.symbols | 149 ++++++++++++++++ .../typeParameterConstModifiers.types | 166 ++++++++++++++++++ .../typeParameterConstModifiers.ts | 50 +++++- 5 files changed, 484 insertions(+), 3 deletions(-) diff --git a/tests/baselines/reference/typeParameterConstModifiers.errors.txt b/tests/baselines/reference/typeParameterConstModifiers.errors.txt index 872f0fe8910a2..c46be5e6f26e6 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.errors.txt +++ b/tests/baselines/reference/typeParameterConstModifiers.errors.txt @@ -130,4 +130,53 @@ typeParameterConstModifiers.ts(55,9): error TS1277: 'const' modifier can only ap const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( { test: 123 } as const, "some string" - ); \ No newline at end of file + ); + + // Same with non-readonly constraint + + function factory_55033_2(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; + } + + const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" + ); + + const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" + ); + + // Repro from https://github.com/microsoft/TypeScript/issues/51931 + + declare function fn(...args: T): T; + + const a = fn("a", false); + + // More examples of non-readonly constraints + + declare function fa1(args: T): T; + declare function fa2(args: T): T; + + fa1(["hello", 42]); + fa2(["hello", 42]); + + declare function fb1(...args: T): T; + declare function fb2(...args: T): T; + + fb1("hello", 42); + fb2("hello", 42); + + declare function fc1(f: (...args: T) => void, ...args: T): T; + declare function fc2(f: (...args: T) => void, ...args: T): T; + + fc1((a: string, b: number) => {}, "hello", 42); + fc2((a: string, b: number) => {}, "hello", 42); + + declare function fn1(...args: T): T; + + fn1({ foo: ["hello", 123] }, { foo: [true]}); + \ No newline at end of file diff --git a/tests/baselines/reference/typeParameterConstModifiers.js b/tests/baselines/reference/typeParameterConstModifiers.js index b52cded2b902d..367f176c7274a 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.js +++ b/tests/baselines/reference/typeParameterConstModifiers.js @@ -124,7 +124,56 @@ const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( { test: 123 } as const, "some string" -); +); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; + +const a = fn("a", false); + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +declare function fa2(args: T): T; + +fa1(["hello", 42]); +fa2(["hello", 42]); + +declare function fb1(...args: T): T; +declare function fb2(...args: T): T; + +fb1("hello", 42); +fb2("hello", 42); + +declare function fc1(f: (...args: T) => void, ...args: T): T; +declare function fc2(f: (...args: T) => void, ...args: T): T; + +fc1((a: string, b: number) => {}, "hello", 42); +fc2((a: string, b: number) => {}, "hello", 42); + +declare function fn1(...args: T): T; + +fn1({ foo: ["hello", 123] }, { foo: [true]}); + //// [typeParameterConstModifiers.js] "use strict"; @@ -193,3 +242,23 @@ function factory_55033(cb) { } var t1_55033 = factory_55033(function (a, b) { })({ test: 123 }, "some string"); var t2_55033 = factory_55033(function (a, b) { })({ test: 123 }, "some string"); +// Same with non-readonly constraint +function factory_55033_2(cb) { + return function call() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return {}; + }; +} +var t1_55033_2 = factory_55033_2(function (a, b) { })({ test: 123 }, "some string"); +var t2_55033_2 = factory_55033_2(function (a, b) { })({ test: 123 }, "some string"); +var a = fn("a", false); +fa1(["hello", 42]); +fa2(["hello", 42]); +fb1("hello", 42); +fb2("hello", 42); +fc1(function (a, b) { }, "hello", 42); +fc2(function (a, b) { }, "hello", 42); +fn1({ foo: ["hello", 123] }, { foo: [true] }); diff --git a/tests/baselines/reference/typeParameterConstModifiers.symbols b/tests/baselines/reference/typeParameterConstModifiers.symbols index 371a38201488f..160a354a105e5 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.symbols +++ b/tests/baselines/reference/typeParameterConstModifiers.symbols @@ -426,3 +426,152 @@ const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( "some string" ); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { +>factory_55033_2 : Symbol(factory_55033_2, Decl(typeParameterConstModifiers.ts, 123, 2)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 127, 25)) +>cb : Symbol(cb, Decl(typeParameterConstModifiers.ts, 127, 52)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 127, 57)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 127, 25)) + + return function call(...args: K): K { +>call : Symbol(call, Decl(typeParameterConstModifiers.ts, 128, 10)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 127, 25)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 128, 44)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) + + return {} as K; +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) + + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t1_55033_2 : Symbol(t1_55033_2, Decl(typeParameterConstModifiers.ts, 133, 5)) +>factory_55033_2 : Symbol(factory_55033_2, Decl(typeParameterConstModifiers.ts, 123, 2)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 133, 36)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 133, 40)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 133, 56)) + + { test: 123 }, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 134, 5)) + + "some string" +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t2_55033_2 : Symbol(t2_55033_2, Decl(typeParameterConstModifiers.ts, 138, 5)) +>factory_55033_2 : Symbol(factory_55033_2, Decl(typeParameterConstModifiers.ts, 123, 2)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 138, 36)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 138, 40)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 138, 56)) + + { test: 123 } as const, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 139, 5)) +>const : Symbol(const) + + "some string" +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; +>fn : Symbol(fn, Decl(typeParameterConstModifiers.ts, 141, 2)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 145, 20)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 145, 43)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 145, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 145, 20)) + +const a = fn("a", false); +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 147, 5)) +>fn : Symbol(fn, Decl(typeParameterConstModifiers.ts, 141, 2)) + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +>fa1 : Symbol(fa1, Decl(typeParameterConstModifiers.ts, 147, 25)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 151, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 151, 48)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 151, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 151, 21)) + +declare function fa2(args: T): T; +>fa2 : Symbol(fa2, Decl(typeParameterConstModifiers.ts, 151, 60)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 152, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 152, 57)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 152, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 152, 21)) + +fa1(["hello", 42]); +>fa1 : Symbol(fa1, Decl(typeParameterConstModifiers.ts, 147, 25)) + +fa2(["hello", 42]); +>fa2 : Symbol(fa2, Decl(typeParameterConstModifiers.ts, 151, 60)) + +declare function fb1(...args: T): T; +>fb1 : Symbol(fb1, Decl(typeParameterConstModifiers.ts, 155, 19)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 157, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 157, 48)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 157, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 157, 21)) + +declare function fb2(...args: T): T; +>fb2 : Symbol(fb2, Decl(typeParameterConstModifiers.ts, 157, 63)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 158, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 158, 57)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 158, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 158, 21)) + +fb1("hello", 42); +>fb1 : Symbol(fb1, Decl(typeParameterConstModifiers.ts, 155, 19)) + +fb2("hello", 42); +>fb2 : Symbol(fb2, Decl(typeParameterConstModifiers.ts, 157, 63)) + +declare function fc1(f: (...args: T) => void, ...args: T): T; +>fc1 : Symbol(fc1, Decl(typeParameterConstModifiers.ts, 161, 17)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) +>f : Symbol(f, Decl(typeParameterConstModifiers.ts, 163, 48)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 163, 52)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 163, 72)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) + +declare function fc2(f: (...args: T) => void, ...args: T): T; +>fc2 : Symbol(fc2, Decl(typeParameterConstModifiers.ts, 163, 88)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) +>f : Symbol(f, Decl(typeParameterConstModifiers.ts, 164, 57)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 164, 61)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 164, 81)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) + +fc1((a: string, b: number) => {}, "hello", 42); +>fc1 : Symbol(fc1, Decl(typeParameterConstModifiers.ts, 161, 17)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 166, 5)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 166, 15)) + +fc2((a: string, b: number) => {}, "hello", 42); +>fc2 : Symbol(fc2, Decl(typeParameterConstModifiers.ts, 163, 88)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 167, 5)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 167, 15)) + +declare function fn1(...args: T): T; +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 167, 47)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 169, 38)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 169, 59)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) + +fn1({ foo: ["hello", 123] }, { foo: [true]}); +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 167, 47)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 171, 5)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 171, 30)) + diff --git a/tests/baselines/reference/typeParameterConstModifiers.types b/tests/baselines/reference/typeParameterConstModifiers.types index 6c87617c96c9a..37dd7c23df911 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.types +++ b/tests/baselines/reference/typeParameterConstModifiers.types @@ -485,3 +485,169 @@ const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( >"some string" : "some string" ); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { +>factory_55033_2 : (cb: (...args: T) => void) => (...args: K) => K +>cb : (...args: T) => void +>args : T + + return function call(...args: K): K { +>function call(...args: K): K { return {} as K; } : (...args: K) => K +>call : (...args: K) => K +>args : K + + return {} as K; +>{} as K : K +>{} : {} + + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t1_55033_2 : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {})( { test: 123 }, "some string") : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033_2 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 }, +>{ test: 123 } : { test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t2_55033_2 : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {})( { test: 123 } as const, "some string") : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033_2 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 } as const, +>{ test: 123 } as const : { readonly test: 123; } +>{ test: 123 } : { readonly test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; +>fn : (...args: T) => T +>args : T + +const a = fn("a", false); +>a : ["a", false] +>fn("a", false) : ["a", false] +>fn : (...args: T) => T +>"a" : "a" +>false : false + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +>fa1 : (args: T) => T +>args : T + +declare function fa2(args: T): T; +>fa2 : (args: T) => T +>args : T + +fa1(["hello", 42]); +>fa1(["hello", 42]) : ["hello", 42] +>fa1 : (args: T) => T +>["hello", 42] : ["hello", 42] +>"hello" : "hello" +>42 : 42 + +fa2(["hello", 42]); +>fa2(["hello", 42]) : readonly ["hello", 42] +>fa2 : (args: T) => T +>["hello", 42] : ["hello", 42] +>"hello" : "hello" +>42 : 42 + +declare function fb1(...args: T): T; +>fb1 : (...args: T) => T +>args : T + +declare function fb2(...args: T): T; +>fb2 : (...args: T) => T +>args : T + +fb1("hello", 42); +>fb1("hello", 42) : ["hello", 42] +>fb1 : (...args: T) => T +>"hello" : "hello" +>42 : 42 + +fb2("hello", 42); +>fb2("hello", 42) : readonly ["hello", 42] +>fb2 : (...args: T) => T +>"hello" : "hello" +>42 : 42 + +declare function fc1(f: (...args: T) => void, ...args: T): T; +>fc1 : (f: (...args: T) => void, ...args: T) => T +>f : (...args: T) => void +>args : T +>args : T + +declare function fc2(f: (...args: T) => void, ...args: T): T; +>fc2 : (f: (...args: T) => void, ...args: T) => T +>f : (...args: T) => void +>args : T +>args : T + +fc1((a: string, b: number) => {}, "hello", 42); +>fc1((a: string, b: number) => {}, "hello", 42) : ["hello", 42] +>fc1 : (f: (...args: T) => void, ...args: T) => T +>(a: string, b: number) => {} : (a: string, b: number) => void +>a : string +>b : number +>"hello" : "hello" +>42 : 42 + +fc2((a: string, b: number) => {}, "hello", 42); +>fc2((a: string, b: number) => {}, "hello", 42) : readonly ["hello", 42] +>fc2 : (f: (...args: T) => void, ...args: T) => T +>(a: string, b: number) => {} : (a: string, b: number) => void +>a : string +>b : number +>"hello" : "hello" +>42 : 42 + +declare function fn1(...args: T): T; +>fn1 : (...args: T) => T +>foo : unknown[] +>args : T + +fn1({ foo: ["hello", 123] }, { foo: [true]}); +>fn1({ foo: ["hello", 123] }, { foo: [true]}) : [{ readonly foo: ["hello", 123]; }, { readonly foo: [true]; }] +>fn1 : (...args: T) => T +>{ foo: ["hello", 123] } : { foo: ["hello", 123]; } +>foo : ["hello", 123] +>["hello", 123] : ["hello", 123] +>"hello" : "hello" +>123 : 123 +>{ foo: [true]} : { foo: [true]; } +>foo : [true] +>[true] : [true] +>true : true + diff --git a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts index 448f007da972d..6a9e78ff7afad 100644 --- a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts +++ b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts @@ -123,4 +123,52 @@ const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( { test: 123 } as const, "some string" -); \ No newline at end of file +); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; + +const a = fn("a", false); + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +declare function fa2(args: T): T; + +fa1(["hello", 42]); +fa2(["hello", 42]); + +declare function fb1(...args: T): T; +declare function fb2(...args: T): T; + +fb1("hello", 42); +fb2("hello", 42); + +declare function fc1(f: (...args: T) => void, ...args: T): T; +declare function fc2(f: (...args: T) => void, ...args: T): T; + +fc1((a: string, b: number) => {}, "hello", 42); +fc2((a: string, b: number) => {}, "hello", 42); + +declare function fn1(...args: T): T; + +fn1({ foo: ["hello", 123] }, { foo: [true]}); From 52fe112b183b4148f2886d2132ce4eff92c6d304 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Aug 2023 10:52:35 -0700 Subject: [PATCH 06/10] More comprehensive check for mutability --- src/compiler/checker.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b8489eeb8fe9d..a000522c08dc7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23265,6 +23265,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } + function isMutableArrayLikeType(type: Type): boolean { + // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, + // or if it is not the undefined or null type and if it is assignable to ReadonlyArray + return isMutableArrayOrTuple(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyArrayType); + } + function getSingleBaseForNonAugmentingSubtype(type: Type) { if (!(getObjectFlags(type) & ObjectFlags.Reference) || !(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)) { return undefined; @@ -23925,7 +23931,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } if (targetRestType) { - callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType) && !isMutableArrayOrTuple(getBaseConstraintOrType(targetRestType))), targetRestType); + callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType) && !isMutableArrayLikeType(targetRestType)), targetRestType); } } @@ -30045,7 +30051,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createTupleType(elementTypes, elementFlags); } if (forceTuple || inConstContext || inTupleContext) { - return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext && !(contextualType && isMutableArrayOrTuple(contextualType)))); + return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext && !(contextualType && isMutableArrayLikeType(contextualType)))); } return createArrayLiteralType(createArrayType(elementTypes.length ? getUnionType(sameMap(elementTypes, (t, i) => elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessTypeOrUndefined(t, numberType) || anyType : t), UnionReduction.Subtype) : @@ -32623,7 +32629,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { names.push((arg as SyntheticExpression).tupleNameSource!); } } - return createTupleType(types, flags, inConstContext && !isMutableArrayOrTuple(getBaseConstraintOrType(restType)), length(names) === length(types) ? names : undefined); + return createTupleType(types, flags, inConstContext && !isMutableArrayLikeType(restType), length(names) === length(types) ? names : undefined); } function checkTypeArguments(signature: Signature, typeArgumentNodes: readonly TypeNode[], reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined { From f1104cc89a8b9bfc0e48c16d30fdc772de3f5938 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 3 Aug 2023 10:52:45 -0700 Subject: [PATCH 07/10] Add more tests --- .../typeParameterConstModifiers.errors.txt | 8 ++++ .../reference/typeParameterConstModifiers.js | 12 ++++++ .../typeParameterConstModifiers.symbols | 40 +++++++++++++++---- .../typeParameterConstModifiers.types | 38 ++++++++++++++++++ .../typeParameterConstModifiers.ts | 8 ++++ 5 files changed, 99 insertions(+), 7 deletions(-) diff --git a/tests/baselines/reference/typeParameterConstModifiers.errors.txt b/tests/baselines/reference/typeParameterConstModifiers.errors.txt index c46be5e6f26e6..2c2599889367a 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.errors.txt +++ b/tests/baselines/reference/typeParameterConstModifiers.errors.txt @@ -176,6 +176,14 @@ typeParameterConstModifiers.ts(55,9): error TS1277: 'const' modifier can only ap fc1((a: string, b: number) => {}, "hello", 42); fc2((a: string, b: number) => {}, "hello", 42); + declare function fd1(args: T): T; + declare function fd2(args: T): T; + + fd1(["hello", "world"]); + fd1([1, 2, 3]); + fd2(["hello", "world"]); + fd2([1, 2, 3]); + declare function fn1(...args: T): T; fn1({ foo: ["hello", 123] }, { foo: [true]}); diff --git a/tests/baselines/reference/typeParameterConstModifiers.js b/tests/baselines/reference/typeParameterConstModifiers.js index 367f176c7274a..5f6ab4ff0e710 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.js +++ b/tests/baselines/reference/typeParameterConstModifiers.js @@ -170,6 +170,14 @@ declare function fc2(f: (...args: T) => void fc1((a: string, b: number) => {}, "hello", 42); fc2((a: string, b: number) => {}, "hello", 42); +declare function fd1(args: T): T; +declare function fd2(args: T): T; + +fd1(["hello", "world"]); +fd1([1, 2, 3]); +fd2(["hello", "world"]); +fd2([1, 2, 3]); + declare function fn1(...args: T): T; fn1({ foo: ["hello", 123] }, { foo: [true]}); @@ -261,4 +269,8 @@ fb1("hello", 42); fb2("hello", 42); fc1(function (a, b) { }, "hello", 42); fc2(function (a, b) { }, "hello", 42); +fd1(["hello", "world"]); +fd1([1, 2, 3]); +fd2(["hello", "world"]); +fd2([1, 2, 3]); fn1({ foo: ["hello", 123] }, { foo: [true] }); diff --git a/tests/baselines/reference/typeParameterConstModifiers.symbols b/tests/baselines/reference/typeParameterConstModifiers.symbols index 160a354a105e5..12c94fc85fb77 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.symbols +++ b/tests/baselines/reference/typeParameterConstModifiers.symbols @@ -562,16 +562,42 @@ fc2((a: string, b: number) => {}, "hello", 42); >a : Symbol(a, Decl(typeParameterConstModifiers.ts, 167, 5)) >b : Symbol(b, Decl(typeParameterConstModifiers.ts, 167, 15)) -declare function fn1(...args: T): T; ->fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 167, 47)) +declare function fd1(args: T): T; +>fd1 : Symbol(fd1, Decl(typeParameterConstModifiers.ts, 167, 47)) >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) ->foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 169, 38)) ->args : Symbol(args, Decl(typeParameterConstModifiers.ts, 169, 59)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 169, 58)) >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) +declare function fd2(args: T): T; +>fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 170, 76)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) + +fd1(["hello", "world"]); +>fd1 : Symbol(fd1, Decl(typeParameterConstModifiers.ts, 167, 47)) + +fd1([1, 2, 3]); +>fd1 : Symbol(fd1, Decl(typeParameterConstModifiers.ts, 167, 47)) + +fd2(["hello", "world"]); +>fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) + +fd2([1, 2, 3]); +>fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) + +declare function fn1(...args: T): T; +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 175, 15)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 177, 21)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 177, 38)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 177, 59)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 177, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 177, 21)) + fn1({ foo: ["hello", 123] }, { foo: [true]}); ->fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 167, 47)) ->foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 171, 5)) ->foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 171, 30)) +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 175, 15)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 179, 5)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 179, 30)) diff --git a/tests/baselines/reference/typeParameterConstModifiers.types b/tests/baselines/reference/typeParameterConstModifiers.types index 37dd7c23df911..79ddb8f8b2476 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.types +++ b/tests/baselines/reference/typeParameterConstModifiers.types @@ -633,6 +633,44 @@ fc2((a: string, b: number) => {}, "hello", 42); >"hello" : "hello" >42 : 42 +declare function fd1(args: T): T; +>fd1 : (args: T) => T +>args : T + +declare function fd2(args: T): T; +>fd2 : (args: T) => T +>args : T + +fd1(["hello", "world"]); +>fd1(["hello", "world"]) : ["hello", "world"] +>fd1 : (args: T) => T +>["hello", "world"] : ["hello", "world"] +>"hello" : "hello" +>"world" : "world" + +fd1([1, 2, 3]); +>fd1([1, 2, 3]) : [1, 2, 3] +>fd1 : (args: T) => T +>[1, 2, 3] : [1, 2, 3] +>1 : 1 +>2 : 2 +>3 : 3 + +fd2(["hello", "world"]); +>fd2(["hello", "world"]) : readonly ["hello", "world"] +>fd2 : (args: T) => T +>["hello", "world"] : ["hello", "world"] +>"hello" : "hello" +>"world" : "world" + +fd2([1, 2, 3]); +>fd2([1, 2, 3]) : readonly [1, 2, 3] +>fd2 : (args: T) => T +>[1, 2, 3] : [1, 2, 3] +>1 : 1 +>2 : 2 +>3 : 3 + declare function fn1(...args: T): T; >fn1 : (...args: T) => T >foo : unknown[] diff --git a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts index 6a9e78ff7afad..16a571232004c 100644 --- a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts +++ b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts @@ -169,6 +169,14 @@ declare function fc2(f: (...args: T) => void fc1((a: string, b: number) => {}, "hello", 42); fc2((a: string, b: number) => {}, "hello", 42); +declare function fd1(args: T): T; +declare function fd2(args: T): T; + +fd1(["hello", "world"]); +fd1([1, 2, 3]); +fd2(["hello", "world"]); +fd2([1, 2, 3]); + declare function fn1(...args: T): T; fn1({ foo: ["hello", 123] }, { foo: [true]}); From 905723d9ea15b4bf5043232a910fdda1f1eaabae Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Aug 2023 10:30:15 -0700 Subject: [PATCH 08/10] Address CR feedback --- src/compiler/checker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a000522c08dc7..2284aa5266ca1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23266,8 +23266,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isMutableArrayLikeType(type: Type): boolean { - // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, - // or if it is not the undefined or null type and if it is assignable to ReadonlyArray + // A type is mutable-array-like if it is a reference to the global Array type, or if it is not the + // undefined or null type and if it is assignable to Array return isMutableArrayOrTuple(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyArrayType); } @@ -23931,7 +23931,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } if (targetRestType) { - callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType) && !isMutableArrayLikeType(targetRestType)), targetRestType); + callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType) && !someType(targetRestType, isMutableArrayLikeType)), targetRestType); } } @@ -30051,7 +30051,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createTupleType(elementTypes, elementFlags); } if (forceTuple || inConstContext || inTupleContext) { - return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext && !(contextualType && isMutableArrayLikeType(contextualType)))); + return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext && !(contextualType && someType(contextualType, isMutableArrayLikeType)))); } return createArrayLiteralType(createArrayType(elementTypes.length ? getUnionType(sameMap(elementTypes, (t, i) => elementFlags[i] & ElementFlags.Variadic ? getIndexedAccessTypeOrUndefined(t, numberType) || anyType : t), UnionReduction.Subtype) : @@ -32629,7 +32629,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { names.push((arg as SyntheticExpression).tupleNameSource!); } } - return createTupleType(types, flags, inConstContext && !isMutableArrayLikeType(restType), length(names) === length(types) ? names : undefined); + return createTupleType(types, flags, inConstContext && !someType(restType, isMutableArrayLikeType), length(names) === length(types) ? names : undefined); } function checkTypeArguments(signature: Signature, typeArgumentNodes: readonly TypeNode[], reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined { From 30281accdbf58f88d3e8ac811861d12e763d1ffe Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 24 Aug 2023 10:41:20 -0700 Subject: [PATCH 09/10] Add more tests --- .../typeParameterConstModifiers.errors.txt | 5 ++- .../reference/typeParameterConstModifiers.js | 7 +++- .../typeParameterConstModifiers.symbols | 35 +++++++++++++------ .../typeParameterConstModifiers.types | 31 ++++++++++++---- .../typeParameterConstModifiers.ts | 5 ++- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/tests/baselines/reference/typeParameterConstModifiers.errors.txt b/tests/baselines/reference/typeParameterConstModifiers.errors.txt index 2c2599889367a..3818707f74751 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.errors.txt +++ b/tests/baselines/reference/typeParameterConstModifiers.errors.txt @@ -177,12 +177,15 @@ typeParameterConstModifiers.ts(55,9): error TS1277: 'const' modifier can only ap fc2((a: string, b: number) => {}, "hello", 42); declare function fd1(args: T): T; - declare function fd2(args: T): T; + declare function fd2(args: T): T; + declare function fd3(args: T): T; fd1(["hello", "world"]); fd1([1, 2, 3]); fd2(["hello", "world"]); fd2([1, 2, 3]); + fd3(["hello", "world"]); + fd3([1, 2, 3]); declare function fn1(...args: T): T; diff --git a/tests/baselines/reference/typeParameterConstModifiers.js b/tests/baselines/reference/typeParameterConstModifiers.js index 5f6ab4ff0e710..9c82ade3c5549 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.js +++ b/tests/baselines/reference/typeParameterConstModifiers.js @@ -171,12 +171,15 @@ fc1((a: string, b: number) => {}, "hello", 42); fc2((a: string, b: number) => {}, "hello", 42); declare function fd1(args: T): T; -declare function fd2(args: T): T; +declare function fd2(args: T): T; +declare function fd3(args: T): T; fd1(["hello", "world"]); fd1([1, 2, 3]); fd2(["hello", "world"]); fd2([1, 2, 3]); +fd3(["hello", "world"]); +fd3([1, 2, 3]); declare function fn1(...args: T): T; @@ -273,4 +276,6 @@ fd1(["hello", "world"]); fd1([1, 2, 3]); fd2(["hello", "world"]); fd2([1, 2, 3]); +fd3(["hello", "world"]); +fd3([1, 2, 3]); fn1({ foo: ["hello", 123] }, { foo: [true] }); diff --git a/tests/baselines/reference/typeParameterConstModifiers.symbols b/tests/baselines/reference/typeParameterConstModifiers.symbols index 12c94fc85fb77..029008e299870 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.symbols +++ b/tests/baselines/reference/typeParameterConstModifiers.symbols @@ -569,13 +569,20 @@ declare function fd1(args: T): T; >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) -declare function fd2(args: T): T; +declare function fd2(args: T): T; >fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) ->args : Symbol(args, Decl(typeParameterConstModifiers.ts, 170, 76)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 170, 67)) >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) >T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) +declare function fd3(args: T): T; +>fd3 : Symbol(fd3, Decl(typeParameterConstModifiers.ts, 170, 79)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 171, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 171, 76)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 171, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 171, 21)) + fd1(["hello", "world"]); >fd1 : Symbol(fd1, Decl(typeParameterConstModifiers.ts, 167, 47)) @@ -588,16 +595,22 @@ fd2(["hello", "world"]); fd2([1, 2, 3]); >fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) +fd3(["hello", "world"]); +>fd3 : Symbol(fd3, Decl(typeParameterConstModifiers.ts, 170, 79)) + +fd3([1, 2, 3]); +>fd3 : Symbol(fd3, Decl(typeParameterConstModifiers.ts, 170, 79)) + declare function fn1(...args: T): T; ->fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 175, 15)) ->T : Symbol(T, Decl(typeParameterConstModifiers.ts, 177, 21)) ->foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 177, 38)) ->args : Symbol(args, Decl(typeParameterConstModifiers.ts, 177, 59)) ->T : Symbol(T, Decl(typeParameterConstModifiers.ts, 177, 21)) ->T : Symbol(T, Decl(typeParameterConstModifiers.ts, 177, 21)) +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 178, 15)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 180, 21)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 180, 38)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 180, 59)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 180, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 180, 21)) fn1({ foo: ["hello", 123] }, { foo: [true]}); ->fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 175, 15)) ->foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 179, 5)) ->foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 179, 30)) +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 178, 15)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 182, 5)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 182, 30)) diff --git a/tests/baselines/reference/typeParameterConstModifiers.types b/tests/baselines/reference/typeParameterConstModifiers.types index 79ddb8f8b2476..5f7e230e14a25 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.types +++ b/tests/baselines/reference/typeParameterConstModifiers.types @@ -637,8 +637,12 @@ declare function fd1(args: T): T; >fd1 : (args: T) => T >args : T -declare function fd2(args: T): T; ->fd2 : (args: T) => T +declare function fd2(args: T): T; +>fd2 : (args: T) => T +>args : T + +declare function fd3(args: T): T; +>fd3 : (args: T) => T >args : T fd1(["hello", "world"]); @@ -657,15 +661,30 @@ fd1([1, 2, 3]); >3 : 3 fd2(["hello", "world"]); ->fd2(["hello", "world"]) : readonly ["hello", "world"] ->fd2 : (args: T) => T +>fd2(["hello", "world"]) : ["hello", "world"] +>fd2 : (args: T) => T >["hello", "world"] : ["hello", "world"] >"hello" : "hello" >"world" : "world" fd2([1, 2, 3]); ->fd2([1, 2, 3]) : readonly [1, 2, 3] ->fd2 : (args: T) => T +>fd2([1, 2, 3]) : [1, 2, 3] +>fd2 : (args: T) => T +>[1, 2, 3] : [1, 2, 3] +>1 : 1 +>2 : 2 +>3 : 3 + +fd3(["hello", "world"]); +>fd3(["hello", "world"]) : readonly ["hello", "world"] +>fd3 : (args: T) => T +>["hello", "world"] : ["hello", "world"] +>"hello" : "hello" +>"world" : "world" + +fd3([1, 2, 3]); +>fd3([1, 2, 3]) : readonly [1, 2, 3] +>fd3 : (args: T) => T >[1, 2, 3] : [1, 2, 3] >1 : 1 >2 : 2 diff --git a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts index 16a571232004c..14afe2219d188 100644 --- a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts +++ b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts @@ -170,12 +170,15 @@ fc1((a: string, b: number) => {}, "hello", 42); fc2((a: string, b: number) => {}, "hello", 42); declare function fd1(args: T): T; -declare function fd2(args: T): T; +declare function fd2(args: T): T; +declare function fd3(args: T): T; fd1(["hello", "world"]); fd1([1, 2, 3]); fd2(["hello", "world"]); fd2([1, 2, 3]); +fd3(["hello", "world"]); +fd3([1, 2, 3]); declare function fn1(...args: T): T; From c148395299892457f8ef6a3be1e97a64e0d81576 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 26 Aug 2023 07:45:17 -0700 Subject: [PATCH 10/10] Exclude any type from isMutableArrayLikeType --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2284aa5266ca1..b37d9c0a5db8b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23267,8 +23267,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isMutableArrayLikeType(type: Type): boolean { // A type is mutable-array-like if it is a reference to the global Array type, or if it is not the - // undefined or null type and if it is assignable to Array - return isMutableArrayOrTuple(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyArrayType); + // any, undefined or null type and if it is assignable to Array + return isMutableArrayOrTuple(type) || !(type.flags & (TypeFlags.Any | TypeFlags.Nullable)) && isTypeAssignableTo(type, anyArrayType); } function getSingleBaseForNonAugmentingSubtype(type: Type) {