From b795c92efe1115357e7f48e372a3d61cb02888d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 1 Jan 2024 16:32:37 +0100 Subject: [PATCH 1/4] Fixed error span for argument checks when the argument is a `SatisfiesExpression` --- src/compiler/checker.ts | 11 +- ...ypeSatisfaction_errorLocations1.errors.txt | 110 +++++++++++ .../typeSatisfaction_errorLocations1.symbols | 108 +++++++++++ .../typeSatisfaction_errorLocations1.types | 182 ++++++++++++++++++ .../typeSatisfaction_errorLocations1.ts | 39 ++++ 5 files changed, 448 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt create mode 100644 tests/baselines/reference/typeSatisfaction_errorLocations1.symbols create mode 100644 tests/baselines/reference/typeSatisfaction_errorLocations1.types create mode 100644 tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d4f288f1293b..8cb3f59582060 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -698,6 +698,7 @@ import { isRightSideOfQualifiedNameOrPropertyAccess, isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName, isSameEntityName, + isSatisfiesExpression, isSetAccessor, isSetAccessorDeclaration, isShorthandAmbientModuleSymbol, @@ -33939,6 +33940,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function getEffectiveCheckArgumentNode(argument: Expression): Expression { + argument = skipParentheses(argument); + return isSatisfiesExpression(argument) ? skipParentheses(argument.expression) : argument; + } + function getSignatureApplicabilityError( node: CallLikeExpression, args: readonly Expression[], @@ -33982,7 +33988,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; - if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage, containingMessageChain, errorOutputContainer)) { + const effectiveCheckArgumentNode = getEffectiveCheckArgumentNode(arg); + if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? effectiveCheckArgumentNode : undefined, effectiveCheckArgumentNode, headMessage, containingMessageChain, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors"); maybeAddMissingAwaitInfo(arg, checkArgType, paramType); return errorOutputContainer.errors || emptyArray; @@ -33994,7 +34001,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const restArgCount = args.length - argCount; const errorNode = !reportErrors ? undefined : restArgCount === 0 ? node : - restArgCount === 1 ? args[argCount] : + restArgCount === 1 ? getEffectiveCheckArgumentNode(args[argCount]) : setTextRangePosEnd(createSyntheticExpression(node, spreadType), args[argCount].pos, args[args.length - 1].end); if (!checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage, /*containingMessageChain*/ undefined, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "rest parameter should have errors when reporting errors"); diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt new file mode 100644 index 0000000000000..72554b858187b --- /dev/null +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt @@ -0,0 +1,110 @@ +typeSatisfaction_errorLocations1.ts(4,5): error TS2345: Argument of type '{}' is not assignable to parameter of type '{ a: true; }'. + Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +typeSatisfaction_errorLocations1.ts(5,7): error TS2322: Type 'number' is not assignable to type 'true'. +typeSatisfaction_errorLocations1.ts(6,5): error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: true; }'. + Types of property 'a' are incompatible. + Type 'number' is not assignable to type 'true'. +typeSatisfaction_errorLocations1.ts(11,10): error TS2345: Argument of type '{}' is not assignable to parameter of type '{ a: true; }'. + Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +typeSatisfaction_errorLocations1.ts(12,12): error TS2322: Type 'number' is not assignable to type 'true'. +typeSatisfaction_errorLocations1.ts(13,10): error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: true; }'. + Types of property 'a' are incompatible. + Type 'number' is not assignable to type 'true'. +typeSatisfaction_errorLocations1.ts(16,5): error TS2345: Argument of type '[{ a: boolean; }]' is not assignable to parameter of type 'T'. + 'T' could be instantiated with an arbitrary type which could be unrelated to '[{ a: boolean; }]'. +typeSatisfaction_errorLocations1.ts(18,5): error TS2345: Argument of type '[{ a: true; }]' is not assignable to parameter of type 'T'. + '[{ a: true; }]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: true; }[]'. +typeSatisfaction_errorLocations1.ts(21,47): error TS2322: Type 'number' is not assignable to type 'boolean'. +typeSatisfaction_errorLocations1.ts(23,23): error TS2322: Type 'boolean' is not assignable to type 'number'. +typeSatisfaction_errorLocations1.ts(25,20): error TS1360: Type 'number' does not satisfy the expected type 'boolean'. +typeSatisfaction_errorLocations1.ts(26,7): error TS2322: Type '1' is not assignable to type 'true'. +typeSatisfaction_errorLocations1.ts(29,18): error TS2322: Type 'string' is not assignable to type 'number'. +typeSatisfaction_errorLocations1.ts(31,20): error TS1360: Type 'readonly [10, "20"]' does not satisfy the expected type 'number[]'. + The type 'readonly [10, "20"]' is 'readonly' and cannot be assigned to the mutable type 'number[]'. +typeSatisfaction_errorLocations1.ts(34,9): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. +typeSatisfaction_errorLocations1.ts(36,9): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. + + +==== typeSatisfaction_errorLocations1.ts (16 errors) ==== + const obj1 = { a: 1 }; + + const fn1 = (s: { a: true }) => {}; + fn1({} satisfies unknown); + ~~ +!!! error TS2345: Argument of type '{}' is not assignable to parameter of type '{ a: true; }'. +!!! error TS2345: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +!!! related TS2728 typeSatisfaction_errorLocations1.ts:3:19: 'a' is declared here. + fn1({ a: 1 } satisfies unknown); + ~ +!!! error TS2322: Type 'number' is not assignable to type 'true'. +!!! related TS6500 typeSatisfaction_errorLocations1.ts:3:19: The expected type comes from property 'a' which is declared here on type '{ a: true; }' + fn1(obj1 satisfies unknown); + ~~~~ +!!! error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: true; }'. +!!! error TS2345: Types of property 'a' are incompatible. +!!! error TS2345: Type 'number' is not assignable to type 'true'. + + class Cls1 { + constructor(p: { a: true }) {} + } + new Cls1({} satisfies unknown); + ~~ +!!! error TS2345: Argument of type '{}' is not assignable to parameter of type '{ a: true; }'. +!!! error TS2345: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +!!! related TS2728 typeSatisfaction_errorLocations1.ts:9:20: 'a' is declared here. + new Cls1({ a: 1 } satisfies unknown); + ~ +!!! error TS2322: Type 'number' is not assignable to type 'true'. +!!! related TS6500 typeSatisfaction_errorLocations1.ts:9:20: The expected type comes from property 'a' which is declared here on type '{ a: true; }' + new Cls1(obj1 satisfies unknown); + ~~~~ +!!! error TS2345: Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: true; }'. +!!! error TS2345: Types of property 'a' are incompatible. +!!! error TS2345: Type 'number' is not assignable to type 'true'. + + function fn2(f: (...args: T) => void) { + f({ a: true } satisfies unknown); + ~~~~~~~~~~~ +!!! error TS2345: Argument of type '[{ a: boolean; }]' is not assignable to parameter of type 'T'. +!!! error TS2345: 'T' could be instantiated with an arbitrary type which could be unrelated to '[{ a: boolean; }]'. + const o = { a: true as const }; + f(o satisfies unknown); + ~ +!!! error TS2345: Argument of type '[{ a: true; }]' is not assignable to parameter of type 'T'. +!!! error TS2345: '[{ a: true; }]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: true; }[]'. + } + + const tuple1: [boolean, boolean] = [true, 100 satisfies unknown]; + ~~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'boolean'. + + const obj2 = { a: 10, b: true } satisfies Record; + ~ +!!! error TS2322: Type 'boolean' is not assignable to type 'number'. + + const literal1 = 1 satisfies boolean; + ~~~~~~~~~ +!!! error TS1360: Type 'number' does not satisfy the expected type 'boolean'. + const literal2: true = 1 satisfies number; + ~~~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'true'. + + declare function fn3(...args: unknown[]): void; + fn3(10, ...([10, "20"] satisfies number[])); + ~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + const tuple2 = [10, "20"] as const; + fn3(10, ...(tuple2 satisfies number[])); + ~~~~~~~~~ +!!! error TS1360: Type 'readonly [10, "20"]' does not satisfy the expected type 'number[]'. +!!! error TS1360: The type 'readonly [10, "20"]' is 'readonly' and cannot be assigned to the mutable type 'number[]'. + + declare function fn4(...args: number[]): void; + fn4(10, ...(["10", "20"] satisfies readonly string[])); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. + const tuple3 = ["10", "20"] as const; + fn4(10, ...(tuple3 satisfies readonly string[])); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. + \ No newline at end of file diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols new file mode 100644 index 0000000000000..2f3beb00a8cb1 --- /dev/null +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols @@ -0,0 +1,108 @@ +//// [tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts] //// + +=== typeSatisfaction_errorLocations1.ts === +const obj1 = { a: 1 }; +>obj1 : Symbol(obj1, Decl(typeSatisfaction_errorLocations1.ts, 0, 5)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 0, 14)) + +const fn1 = (s: { a: true }) => {}; +>fn1 : Symbol(fn1, Decl(typeSatisfaction_errorLocations1.ts, 2, 5)) +>s : Symbol(s, Decl(typeSatisfaction_errorLocations1.ts, 2, 13)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 2, 17)) + +fn1({} satisfies unknown); +>fn1 : Symbol(fn1, Decl(typeSatisfaction_errorLocations1.ts, 2, 5)) + +fn1({ a: 1 } satisfies unknown); +>fn1 : Symbol(fn1, Decl(typeSatisfaction_errorLocations1.ts, 2, 5)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 4, 5)) + +fn1(obj1 satisfies unknown); +>fn1 : Symbol(fn1, Decl(typeSatisfaction_errorLocations1.ts, 2, 5)) +>obj1 : Symbol(obj1, Decl(typeSatisfaction_errorLocations1.ts, 0, 5)) + +class Cls1 { +>Cls1 : Symbol(Cls1, Decl(typeSatisfaction_errorLocations1.ts, 5, 28)) + + constructor(p: { a: true }) {} +>p : Symbol(p, Decl(typeSatisfaction_errorLocations1.ts, 8, 14)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 8, 18)) +} +new Cls1({} satisfies unknown); +>Cls1 : Symbol(Cls1, Decl(typeSatisfaction_errorLocations1.ts, 5, 28)) + +new Cls1({ a: 1 } satisfies unknown); +>Cls1 : Symbol(Cls1, Decl(typeSatisfaction_errorLocations1.ts, 5, 28)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 11, 10)) + +new Cls1(obj1 satisfies unknown); +>Cls1 : Symbol(Cls1, Decl(typeSatisfaction_errorLocations1.ts, 5, 28)) +>obj1 : Symbol(obj1, Decl(typeSatisfaction_errorLocations1.ts, 0, 5)) + +function fn2(f: (...args: T) => void) { +>fn2 : Symbol(fn2, Decl(typeSatisfaction_errorLocations1.ts, 12, 33)) +>T : Symbol(T, Decl(typeSatisfaction_errorLocations1.ts, 14, 13)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 14, 24)) +>f : Symbol(f, Decl(typeSatisfaction_errorLocations1.ts, 14, 38)) +>args : Symbol(args, Decl(typeSatisfaction_errorLocations1.ts, 14, 42)) +>T : Symbol(T, Decl(typeSatisfaction_errorLocations1.ts, 14, 13)) + + f({ a: true } satisfies unknown); +>f : Symbol(f, Decl(typeSatisfaction_errorLocations1.ts, 14, 38)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 15, 5)) + + const o = { a: true as const }; +>o : Symbol(o, Decl(typeSatisfaction_errorLocations1.ts, 16, 7)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 16, 13)) +>const : Symbol(const) + + f(o satisfies unknown); +>f : Symbol(f, Decl(typeSatisfaction_errorLocations1.ts, 14, 38)) +>o : Symbol(o, Decl(typeSatisfaction_errorLocations1.ts, 16, 7)) +} + +const tuple1: [boolean, boolean] = [true, 100 satisfies unknown]; +>tuple1 : Symbol(tuple1, Decl(typeSatisfaction_errorLocations1.ts, 20, 5)) + +const obj2 = { a: 10, b: true } satisfies Record; +>obj2 : Symbol(obj2, Decl(typeSatisfaction_errorLocations1.ts, 22, 5)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 22, 14)) +>b : Symbol(b, Decl(typeSatisfaction_errorLocations1.ts, 22, 21)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +const literal1 = 1 satisfies boolean; +>literal1 : Symbol(literal1, Decl(typeSatisfaction_errorLocations1.ts, 24, 5)) + +const literal2: true = 1 satisfies number; +>literal2 : Symbol(literal2, Decl(typeSatisfaction_errorLocations1.ts, 25, 5)) + +declare function fn3(...args: unknown[]): void; +>fn3 : Symbol(fn3, Decl(typeSatisfaction_errorLocations1.ts, 25, 42)) +>args : Symbol(args, Decl(typeSatisfaction_errorLocations1.ts, 27, 21)) + +fn3(10, ...([10, "20"] satisfies number[])); +>fn3 : Symbol(fn3, Decl(typeSatisfaction_errorLocations1.ts, 25, 42)) + +const tuple2 = [10, "20"] as const; +>tuple2 : Symbol(tuple2, Decl(typeSatisfaction_errorLocations1.ts, 29, 5)) +>const : Symbol(const) + +fn3(10, ...(tuple2 satisfies number[])); +>fn3 : Symbol(fn3, Decl(typeSatisfaction_errorLocations1.ts, 25, 42)) +>tuple2 : Symbol(tuple2, Decl(typeSatisfaction_errorLocations1.ts, 29, 5)) + +declare function fn4(...args: number[]): void; +>fn4 : Symbol(fn4, Decl(typeSatisfaction_errorLocations1.ts, 30, 40)) +>args : Symbol(args, Decl(typeSatisfaction_errorLocations1.ts, 32, 21)) + +fn4(10, ...(["10", "20"] satisfies readonly string[])); +>fn4 : Symbol(fn4, Decl(typeSatisfaction_errorLocations1.ts, 30, 40)) + +const tuple3 = ["10", "20"] as const; +>tuple3 : Symbol(tuple3, Decl(typeSatisfaction_errorLocations1.ts, 34, 5)) +>const : Symbol(const) + +fn4(10, ...(tuple3 satisfies readonly string[])); +>fn4 : Symbol(fn4, Decl(typeSatisfaction_errorLocations1.ts, 30, 40)) +>tuple3 : Symbol(tuple3, Decl(typeSatisfaction_errorLocations1.ts, 34, 5)) + diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.types b/tests/baselines/reference/typeSatisfaction_errorLocations1.types new file mode 100644 index 0000000000000..84696d282fc76 --- /dev/null +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.types @@ -0,0 +1,182 @@ +//// [tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts] //// + +=== typeSatisfaction_errorLocations1.ts === +const obj1 = { a: 1 }; +>obj1 : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +const fn1 = (s: { a: true }) => {}; +>fn1 : (s: { a: true;}) => void +>(s: { a: true }) => {} : (s: { a: true;}) => void +>s : { a: true; } +>a : true +>true : true + +fn1({} satisfies unknown); +>fn1({} satisfies unknown) : void +>fn1 : (s: { a: true; }) => void +>{} satisfies unknown : {} +>{} : {} + +fn1({ a: 1 } satisfies unknown); +>fn1({ a: 1 } satisfies unknown) : void +>fn1 : (s: { a: true; }) => void +>{ a: 1 } satisfies unknown : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +fn1(obj1 satisfies unknown); +>fn1(obj1 satisfies unknown) : void +>fn1 : (s: { a: true; }) => void +>obj1 satisfies unknown : { a: number; } +>obj1 : { a: number; } + +class Cls1 { +>Cls1 : Cls1 + + constructor(p: { a: true }) {} +>p : { a: true; } +>a : true +>true : true +} +new Cls1({} satisfies unknown); +>new Cls1({} satisfies unknown) : Cls1 +>Cls1 : typeof Cls1 +>{} satisfies unknown : {} +>{} : {} + +new Cls1({ a: 1 } satisfies unknown); +>new Cls1({ a: 1 } satisfies unknown) : Cls1 +>Cls1 : typeof Cls1 +>{ a: 1 } satisfies unknown : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +new Cls1(obj1 satisfies unknown); +>new Cls1(obj1 satisfies unknown) : Cls1 +>Cls1 : typeof Cls1 +>obj1 satisfies unknown : { a: number; } +>obj1 : { a: number; } + +function fn2(f: (...args: T) => void) { +>fn2 : (f: (...args: T) => void) => void +>a : true +>true : true +>f : (...args: T) => void +>args : T + + f({ a: true } satisfies unknown); +>f({ a: true } satisfies unknown) : void +>f : (...args: T) => void +>{ a: true } satisfies unknown : { a: boolean; } +>{ a: true } : { a: boolean; } +>a : boolean +>true : true + + const o = { a: true as const }; +>o : { a: true; } +>{ a: true as const } : { a: true; } +>a : true +>true as const : true +>true : true + + f(o satisfies unknown); +>f(o satisfies unknown) : void +>f : (...args: T) => void +>o satisfies unknown : { a: true; } +>o : { a: true; } +} + +const tuple1: [boolean, boolean] = [true, 100 satisfies unknown]; +>tuple1 : [boolean, boolean] +>[true, 100 satisfies unknown] : [true, number] +>true : true +>100 satisfies unknown : 100 +>100 : 100 + +const obj2 = { a: 10, b: true } satisfies Record; +>obj2 : { a: number; b: boolean; } +>{ a: 10, b: true } satisfies Record : { a: number; b: boolean; } +>{ a: 10, b: true } : { a: number; b: boolean; } +>a : number +>10 : 10 +>b : boolean +>true : true + +const literal1 = 1 satisfies boolean; +>literal1 : 1 +>1 satisfies boolean : 1 +>1 : 1 + +const literal2: true = 1 satisfies number; +>literal2 : true +>true : true +>1 satisfies number : 1 +>1 : 1 + +declare function fn3(...args: unknown[]): void; +>fn3 : (...args: unknown[]) => void +>args : unknown[] + +fn3(10, ...([10, "20"] satisfies number[])); +>fn3(10, ...([10, "20"] satisfies number[])) : void +>fn3 : (...args: unknown[]) => void +>10 : 10 +>...([10, "20"] satisfies number[]) : string | number +>([10, "20"] satisfies number[]) : (string | number)[] +>[10, "20"] satisfies number[] : (string | number)[] +>[10, "20"] : (string | number)[] +>10 : 10 +>"20" : "20" + +const tuple2 = [10, "20"] as const; +>tuple2 : readonly [10, "20"] +>[10, "20"] as const : readonly [10, "20"] +>[10, "20"] : readonly [10, "20"] +>10 : 10 +>"20" : "20" + +fn3(10, ...(tuple2 satisfies number[])); +>fn3(10, ...(tuple2 satisfies number[])) : void +>fn3 : (...args: unknown[]) => void +>10 : 10 +>...(tuple2 satisfies number[]) : 10 | "20" +>(tuple2 satisfies number[]) : readonly [10, "20"] +>tuple2 satisfies number[] : readonly [10, "20"] +>tuple2 : readonly [10, "20"] + +declare function fn4(...args: number[]): void; +>fn4 : (...args: number[]) => void +>args : number[] + +fn4(10, ...(["10", "20"] satisfies readonly string[])); +>fn4(10, ...(["10", "20"] satisfies readonly string[])) : void +>fn4 : (...args: number[]) => void +>10 : 10 +>...(["10", "20"] satisfies readonly string[]) : string +>(["10", "20"] satisfies readonly string[]) : string[] +>["10", "20"] satisfies readonly string[] : string[] +>["10", "20"] : string[] +>"10" : "10" +>"20" : "20" + +const tuple3 = ["10", "20"] as const; +>tuple3 : readonly ["10", "20"] +>["10", "20"] as const : readonly ["10", "20"] +>["10", "20"] : readonly ["10", "20"] +>"10" : "10" +>"20" : "20" + +fn4(10, ...(tuple3 satisfies readonly string[])); +>fn4(10, ...(tuple3 satisfies readonly string[])) : void +>fn4 : (...args: number[]) => void +>10 : 10 +>...(tuple3 satisfies readonly string[]) : "20" | "10" +>(tuple3 satisfies readonly string[]) : readonly ["10", "20"] +>tuple3 satisfies readonly string[] : readonly ["10", "20"] +>tuple3 : readonly ["10", "20"] + diff --git a/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts new file mode 100644 index 0000000000000..d7198b0f19adf --- /dev/null +++ b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts @@ -0,0 +1,39 @@ +// @strict: true +// @noEmit: true + +const obj1 = { a: 1 }; + +const fn1 = (s: { a: true }) => {}; +fn1({} satisfies unknown); +fn1({ a: 1 } satisfies unknown); +fn1(obj1 satisfies unknown); + +class Cls1 { + constructor(p: { a: true }) {} +} +new Cls1({} satisfies unknown); +new Cls1({ a: 1 } satisfies unknown); +new Cls1(obj1 satisfies unknown); + +function fn2(f: (...args: T) => void) { + f({ a: true } satisfies unknown); + const o = { a: true as const }; + f(o satisfies unknown); +} + +const tuple1: [boolean, boolean] = [true, 100 satisfies unknown]; + +const obj2 = { a: 10, b: true } satisfies Record; + +const literal1 = 1 satisfies boolean; +const literal2: true = 1 satisfies number; + +declare function fn3(...args: unknown[]): void; +fn3(10, ...([10, "20"] satisfies number[])); +const tuple2 = [10, "20"] as const; +fn3(10, ...(tuple2 satisfies number[])); + +declare function fn4(...args: number[]): void; +fn4(10, ...(["10", "20"] satisfies readonly string[])); +const tuple3 = ["10", "20"] as const; +fn4(10, ...(tuple3 satisfies readonly string[])); From 13c28827a0f8189e8818fbe8f01838179e37c9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 1 Jan 2024 16:44:38 +0100 Subject: [PATCH 2/4] add extra test cases --- ...ypeSatisfaction_errorLocations1.errors.txt | 19 ++++++++++++++++++- .../typeSatisfaction_errorLocations1.symbols | 12 ++++++++++++ .../typeSatisfaction_errorLocations1.types | 16 ++++++++++++++++ .../typeSatisfaction_errorLocations1.ts | 8 ++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt index 72554b858187b..92bd08cdfbab8 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt @@ -23,9 +23,12 @@ typeSatisfaction_errorLocations1.ts(31,20): error TS1360: Type 'readonly [10, "2 The type 'readonly [10, "20"]' is 'readonly' and cannot be assigned to the mutable type 'number[]'. typeSatisfaction_errorLocations1.ts(34,9): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. typeSatisfaction_errorLocations1.ts(36,9): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. +typeSatisfaction_errorLocations1.ts(39,3): error TS2322: Type 'string' is not assignable to type 'number'. +typeSatisfaction_errorLocations1.ts(43,3): error TS2322: Type 'string' is not assignable to type 'number'. +typeSatisfaction_errorLocations1.ts(43,16): error TS1360: Type 'string' does not satisfy the expected type 'number'. -==== typeSatisfaction_errorLocations1.ts (16 errors) ==== +==== typeSatisfaction_errorLocations1.ts (19 errors) ==== const obj1 = { a: 1 }; const fn1 = (s: { a: true }) => {}; @@ -107,4 +110,18 @@ typeSatisfaction_errorLocations1.ts(36,9): error TS2345: Argument of type 'strin fn4(10, ...(tuple3 satisfies readonly string[])); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. + + function fn5(): number { + return "foo" satisfies unknown; + ~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + } + + function fn6(): number { + return "foo" satisfies number; + ~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + ~~~~~~~~~ +!!! error TS1360: Type 'string' does not satisfy the expected type 'number'. + } \ No newline at end of file diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols index 2f3beb00a8cb1..d752ee082c630 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols @@ -106,3 +106,15 @@ fn4(10, ...(tuple3 satisfies readonly string[])); >fn4 : Symbol(fn4, Decl(typeSatisfaction_errorLocations1.ts, 30, 40)) >tuple3 : Symbol(tuple3, Decl(typeSatisfaction_errorLocations1.ts, 34, 5)) +function fn5(): number { +>fn5 : Symbol(fn5, Decl(typeSatisfaction_errorLocations1.ts, 35, 49)) + + return "foo" satisfies unknown; +} + +function fn6(): number { +>fn6 : Symbol(fn6, Decl(typeSatisfaction_errorLocations1.ts, 39, 1)) + + return "foo" satisfies number; +} + diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.types b/tests/baselines/reference/typeSatisfaction_errorLocations1.types index 84696d282fc76..181d7ce952dbf 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.types +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.types @@ -180,3 +180,19 @@ fn4(10, ...(tuple3 satisfies readonly string[])); >tuple3 satisfies readonly string[] : readonly ["10", "20"] >tuple3 : readonly ["10", "20"] +function fn5(): number { +>fn5 : () => number + + return "foo" satisfies unknown; +>"foo" satisfies unknown : "foo" +>"foo" : "foo" +} + +function fn6(): number { +>fn6 : () => number + + return "foo" satisfies number; +>"foo" satisfies number : "foo" +>"foo" : "foo" +} + diff --git a/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts index d7198b0f19adf..9217f5912560c 100644 --- a/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts +++ b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts @@ -37,3 +37,11 @@ declare function fn4(...args: number[]): void; fn4(10, ...(["10", "20"] satisfies readonly string[])); const tuple3 = ["10", "20"] as const; fn4(10, ...(tuple3 satisfies readonly string[])); + +function fn5(): number { + return "foo" satisfies unknown; +} + +function fn6(): number { + return "foo" satisfies number; +} From e0c2268cbe76ae16b5809383f8b3ad5bcc30c6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 1 Jan 2024 23:48:07 +0100 Subject: [PATCH 3/4] Fixed arrow functions --- src/compiler/checker.ts | 13 ++++---- ...ypeSatisfaction_errorLocations1.errors.txt | 21 ++++++++++++- .../typeSatisfaction_errorLocations1.symbols | 11 +++++++ .../typeSatisfaction_errorLocations1.types | 31 +++++++++++++++++++ .../typeSatisfaction_errorLocations1.ts | 4 +++ 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8cb3f59582060..dc0292a137165 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33940,7 +33940,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getEffectiveCheckArgumentNode(argument: Expression): Expression { + function getEffectiveCheckNode(argument: Expression): Expression { argument = skipParentheses(argument); return isSatisfiesExpression(argument) ? skipParentheses(argument.expression) : argument; } @@ -33988,7 +33988,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; - const effectiveCheckArgumentNode = getEffectiveCheckArgumentNode(arg); + const effectiveCheckArgumentNode = getEffectiveCheckNode(arg); if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? effectiveCheckArgumentNode : undefined, effectiveCheckArgumentNode, headMessage, containingMessageChain, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors"); maybeAddMissingAwaitInfo(arg, checkArgType, paramType); @@ -34001,7 +34001,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const restArgCount = args.length - argCount; const errorNode = !reportErrors ? undefined : restArgCount === 0 ? node : - restArgCount === 1 ? getEffectiveCheckArgumentNode(args[argCount]) : + restArgCount === 1 ? getEffectiveCheckNode(args[argCount]) : setTextRangePosEnd(createSyntheticExpression(node, spreadType), args[argCount].pos, args[args.length - 1].end); if (!checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage, /*containingMessageChain*/ undefined, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "rest parameter should have errors when reporting errors"); @@ -37292,12 +37292,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const exprType = checkExpression(node.body); const returnOrPromisedType = returnType && unwrapReturnType(returnType, functionFlags); if (returnOrPromisedType) { + const effectiveCheckNode = getEffectiveCheckNode(node.body); if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function - const awaitedType = checkAwaitedType(exprType, /*withAlias*/ false, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, node.body, node.body); + const awaitedType = checkAwaitedType(exprType, /*withAlias*/ false, effectiveCheckNode, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); + checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, effectiveCheckNode, effectiveCheckNode); } else { // Normal function - checkTypeAssignableToAndOptionallyElaborate(exprType, returnOrPromisedType, node.body, node.body); + checkTypeAssignableToAndOptionallyElaborate(exprType, returnOrPromisedType, effectiveCheckNode, effectiveCheckNode); } } } diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt index 92bd08cdfbab8..a776207c75d88 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt @@ -26,9 +26,14 @@ typeSatisfaction_errorLocations1.ts(36,9): error TS2345: Argument of type 'strin typeSatisfaction_errorLocations1.ts(39,3): error TS2322: Type 'string' is not assignable to type 'number'. typeSatisfaction_errorLocations1.ts(43,3): error TS2322: Type 'string' is not assignable to type 'number'. typeSatisfaction_errorLocations1.ts(43,16): error TS1360: Type 'string' does not satisfy the expected type 'number'. +typeSatisfaction_errorLocations1.ts(46,22): error TS2741: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +typeSatisfaction_errorLocations1.ts(47,24): error TS2322: Type 'number' is not assignable to type 'true'. +typeSatisfaction_errorLocations1.ts(48,21): error TS2322: Type '{ a: number; }' is not assignable to type '{ a: true; }'. + Types of property 'a' are incompatible. + Type 'number' is not assignable to type 'true'. -==== typeSatisfaction_errorLocations1.ts (19 errors) ==== +==== typeSatisfaction_errorLocations1.ts (22 errors) ==== const obj1 = { a: 1 }; const fn1 = (s: { a: true }) => {}; @@ -124,4 +129,18 @@ typeSatisfaction_errorLocations1.ts(43,16): error TS1360: Type 'string' does not ~~~~~~~~~ !!! error TS1360: Type 'string' does not satisfy the expected type 'number'. } + + ((): { a: true } => ({}) satisfies unknown)(); + ~~ +!!! error TS2741: Property 'a' is missing in type '{}' but required in type '{ a: true; }'. +!!! related TS2728 typeSatisfaction_errorLocations1.ts:46:8: 'a' is declared here. + ((): { a: true } => ({ a: 1 }) satisfies unknown)(); + ~ +!!! error TS2322: Type 'number' is not assignable to type 'true'. +!!! related TS6500 typeSatisfaction_errorLocations1.ts:47:8: The expected type comes from property 'a' which is declared here on type '{ a: true; }' + ((): { a: true } => obj1 satisfies unknown)(); + ~~~~ +!!! error TS2322: Type '{ a: number; }' is not assignable to type '{ a: true; }'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'true'. \ No newline at end of file diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols index d752ee082c630..f44c4deaa529e 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.symbols @@ -118,3 +118,14 @@ function fn6(): number { return "foo" satisfies number; } +((): { a: true } => ({}) satisfies unknown)(); +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 45, 6)) + +((): { a: true } => ({ a: 1 }) satisfies unknown)(); +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 46, 6)) +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 46, 22)) + +((): { a: true } => obj1 satisfies unknown)(); +>a : Symbol(a, Decl(typeSatisfaction_errorLocations1.ts, 47, 6)) +>obj1 : Symbol(obj1, Decl(typeSatisfaction_errorLocations1.ts, 0, 5)) + diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.types b/tests/baselines/reference/typeSatisfaction_errorLocations1.types index 181d7ce952dbf..e8e6de7184b2f 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.types +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.types @@ -196,3 +196,34 @@ function fn6(): number { >"foo" : "foo" } +((): { a: true } => ({}) satisfies unknown)(); +>((): { a: true } => ({}) satisfies unknown)() : { a: true; } +>((): { a: true } => ({}) satisfies unknown) : () => { a: true;} +>(): { a: true } => ({}) satisfies unknown : () => { a: true;} +>a : true +>true : true +>({}) satisfies unknown : {} +>({}) : {} +>{} : {} + +((): { a: true } => ({ a: 1 }) satisfies unknown)(); +>((): { a: true } => ({ a: 1 }) satisfies unknown)() : { a: true; } +>((): { a: true } => ({ a: 1 }) satisfies unknown) : () => { a: true;} +>(): { a: true } => ({ a: 1 }) satisfies unknown : () => { a: true;} +>a : true +>true : true +>({ a: 1 }) satisfies unknown : { a: number; } +>({ a: 1 }) : { a: number; } +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +((): { a: true } => obj1 satisfies unknown)(); +>((): { a: true } => obj1 satisfies unknown)() : { a: true; } +>((): { a: true } => obj1 satisfies unknown) : () => { a: true;} +>(): { a: true } => obj1 satisfies unknown : () => { a: true;} +>a : true +>true : true +>obj1 satisfies unknown : { a: number; } +>obj1 : { a: number; } + diff --git a/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts index 9217f5912560c..11276d5ca26c9 100644 --- a/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts +++ b/tests/cases/conformance/expressions/typeSatisfaction/typeSatisfaction_errorLocations1.ts @@ -45,3 +45,7 @@ function fn5(): number { function fn6(): number { return "foo" satisfies number; } + +((): { a: true } => ({}) satisfies unknown)(); +((): { a: true } => ({ a: 1 }) satisfies unknown)(); +((): { a: true } => obj1 satisfies unknown)(); From ffc742625646557d49f999c5a436a32eb766f050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 2 Jan 2024 21:37:45 +0100 Subject: [PATCH 4/4] fix the same thing for tuple elements --- src/compiler/checker.ts | 3 ++- .../reference/typeSatisfaction_errorLocations1.errors.txt | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dc0292a137165..e661f597309b3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20543,7 +20543,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const elem = node.elements[i]; if (isOmittedExpression(elem)) continue; const nameType = getNumberLiteralType(i); - yield { errorNode: elem, innerExpression: elem, nameType }; + const checkNode = getEffectiveCheckNode(elem); + yield { errorNode: checkNode, innerExpression: checkNode, nameType }; } } diff --git a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt index a776207c75d88..0d31158d64227 100644 --- a/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt +++ b/tests/baselines/reference/typeSatisfaction_errorLocations1.errors.txt @@ -14,7 +14,7 @@ typeSatisfaction_errorLocations1.ts(16,5): error TS2345: Argument of type '[{ a: 'T' could be instantiated with an arbitrary type which could be unrelated to '[{ a: boolean; }]'. typeSatisfaction_errorLocations1.ts(18,5): error TS2345: Argument of type '[{ a: true; }]' is not assignable to parameter of type 'T'. '[{ a: true; }]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: true; }[]'. -typeSatisfaction_errorLocations1.ts(21,47): error TS2322: Type 'number' is not assignable to type 'boolean'. +typeSatisfaction_errorLocations1.ts(21,43): error TS2322: Type 'number' is not assignable to type 'boolean'. typeSatisfaction_errorLocations1.ts(23,23): error TS2322: Type 'boolean' is not assignable to type 'number'. typeSatisfaction_errorLocations1.ts(25,20): error TS1360: Type 'number' does not satisfy the expected type 'boolean'. typeSatisfaction_errorLocations1.ts(26,7): error TS2322: Type '1' is not assignable to type 'true'. @@ -83,7 +83,7 @@ typeSatisfaction_errorLocations1.ts(48,21): error TS2322: Type '{ a: number; }' } const tuple1: [boolean, boolean] = [true, 100 satisfies unknown]; - ~~~~~~~~~ + ~~~ !!! error TS2322: Type 'number' is not assignable to type 'boolean'. const obj2 = { a: 10, b: true } satisfies Record;