Skip to content

Commit

Permalink
Fixed incorrect SignatureFlags.HasRestParameter propagation when co…
Browse files Browse the repository at this point in the history
…mbining signatures (#58440)
  • Loading branch information
Andarist authored Sep 25, 2024
1 parent 413f0fa commit 55886a1
Show file tree
Hide file tree
Showing 21 changed files with 1,689 additions and 3 deletions.
17 changes: 14 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14032,8 +14032,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
paramMapper = createTypeMapper(right.typeParameters, left.typeParameters);
// We just use the type parameter defaults from the first signature
}
let flags = (left.flags | right.flags) & (SignatureFlags.PropagatingFlags & ~SignatureFlags.HasRestParameter);
const declaration = left.declaration;
const params = combineUnionParameters(left, right, paramMapper);
const lastParam = lastOrUndefined(params);
if (lastParam && getCheckFlags(lastParam) & CheckFlags.RestParameter) {
flags |= SignatureFlags.HasRestParameter;
}
const thisParam = combineUnionThisParam(left.thisParameter, right.thisParameter, paramMapper);
const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount);
const result = createSignature(
Expand All @@ -14044,7 +14049,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
/*resolvedReturnType*/ undefined,
/*resolvedTypePredicate*/ undefined,
minArgCount,
(left.flags | right.flags) & SignatureFlags.PropagatingFlags,
flags,
);
result.compositeKind = TypeFlags.Union;
result.compositeSignatures = concatenate(left.compositeKind !== TypeFlags.Intersection && left.compositeSignatures || [left], [right]);
Expand Down Expand Up @@ -32545,12 +32550,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const paramSymbol = createSymbol(
SymbolFlags.FunctionScopedVariable | (isOptional && !isRestParam ? SymbolFlags.Optional : 0),
paramName || `arg${i}` as __String,
isRestParam ? CheckFlags.RestParameter : isOptional ? CheckFlags.OptionalParameter : 0,
);
paramSymbol.links.type = isRestParam ? createArrayType(unionParamType) : unionParamType;
params[i] = paramSymbol;
}
if (needsExtraRestElement) {
const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String);
const restParamSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "args" as __String, CheckFlags.RestParameter);
restParamSymbol.links.type = createArrayType(getTypeAtPosition(shorter, longestCount));
if (shorter === right) {
restParamSymbol.links.type = instantiateType(restParamSymbol.links.type, mapper);
Expand All @@ -32567,8 +32573,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
paramMapper = createTypeMapper(right.typeParameters, left.typeParameters);
// We just use the type parameter defaults from the first signature
}
let flags = (left.flags | right.flags) & (SignatureFlags.PropagatingFlags & ~SignatureFlags.HasRestParameter);
const declaration = left.declaration;
const params = combineIntersectionParameters(left, right, paramMapper);
const lastParam = lastOrUndefined(params);
if (lastParam && getCheckFlags(lastParam) & CheckFlags.RestParameter) {
flags |= SignatureFlags.HasRestParameter;
}
const thisParam = combineIntersectionThisParam(left.thisParameter, right.thisParameter, paramMapper);
const minArgCount = Math.max(left.minArgumentCount, right.minArgumentCount);
const result = createSignature(
Expand All @@ -32579,7 +32590,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
/*resolvedReturnType*/ undefined,
/*resolvedTypePredicate*/ undefined,
minArgCount,
(left.flags | right.flags) & SignatureFlags.PropagatingFlags,
flags,
);
result.compositeKind = TypeFlags.Intersection;
result.compositeSignatures = concatenate(left.compositeKind === TypeFlags.Intersection && left.compositeSignatures || [left], [right]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
signatureCombiningRestParameters1.ts(17,6): error TS2345: Argument of type 'any' is not assignable to parameter of type 'never'.


==== signatureCombiningRestParameters1.ts (1 errors) ====
// https://github.com/microsoft/TypeScript/issues/58371

type T1 = "A" | "B";

type T2 = {
C: [string];
D: [number];
};

declare const map: {
[K in T1 | keyof T2]: (...args: K extends keyof T2 ? T2[K] : []) => unknown;
};

declare const args: any;

for (const [key, fn] of Object.entries(map)) {
fn(...args);
~~~~~~~
!!! error TS2345: Argument of type 'any' is not assignable to parameter of type 'never'.
}

const test2: ((a: number, ...args: []) => void) &
((b: string) => void) &
((c: boolean) => void) = (arg) => {};

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//// [tests/cases/compiler/signatureCombiningRestParameters1.ts] ////

=== signatureCombiningRestParameters1.ts ===
// https://github.com/microsoft/TypeScript/issues/58371

type T1 = "A" | "B";
>T1 : Symbol(T1, Decl(signatureCombiningRestParameters1.ts, 0, 0))

type T2 = {
>T2 : Symbol(T2, Decl(signatureCombiningRestParameters1.ts, 2, 20))

C: [string];
>C : Symbol(C, Decl(signatureCombiningRestParameters1.ts, 4, 11))

D: [number];
>D : Symbol(D, Decl(signatureCombiningRestParameters1.ts, 5, 14))

};

declare const map: {
>map : Symbol(map, Decl(signatureCombiningRestParameters1.ts, 9, 13))

[K in T1 | keyof T2]: (...args: K extends keyof T2 ? T2[K] : []) => unknown;
>K : Symbol(K, Decl(signatureCombiningRestParameters1.ts, 10, 3))
>T1 : Symbol(T1, Decl(signatureCombiningRestParameters1.ts, 0, 0))
>T2 : Symbol(T2, Decl(signatureCombiningRestParameters1.ts, 2, 20))
>args : Symbol(args, Decl(signatureCombiningRestParameters1.ts, 10, 25))
>K : Symbol(K, Decl(signatureCombiningRestParameters1.ts, 10, 3))
>T2 : Symbol(T2, Decl(signatureCombiningRestParameters1.ts, 2, 20))
>T2 : Symbol(T2, Decl(signatureCombiningRestParameters1.ts, 2, 20))
>K : Symbol(K, Decl(signatureCombiningRestParameters1.ts, 10, 3))

};

declare const args: any;
>args : Symbol(args, Decl(signatureCombiningRestParameters1.ts, 13, 13))

for (const [key, fn] of Object.entries(map)) {
>key : Symbol(key, Decl(signatureCombiningRestParameters1.ts, 15, 12))
>fn : Symbol(fn, Decl(signatureCombiningRestParameters1.ts, 15, 16))
>Object.entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>entries : Symbol(ObjectConstructor.entries, Decl(lib.es2017.object.d.ts, --, --), Decl(lib.es2017.object.d.ts, --, --))
>map : Symbol(map, Decl(signatureCombiningRestParameters1.ts, 9, 13))

fn(...args);
>fn : Symbol(fn, Decl(signatureCombiningRestParameters1.ts, 15, 16))
>args : Symbol(args, Decl(signatureCombiningRestParameters1.ts, 13, 13))
}

const test2: ((a: number, ...args: []) => void) &
>test2 : Symbol(test2, Decl(signatureCombiningRestParameters1.ts, 19, 5))
>a : Symbol(a, Decl(signatureCombiningRestParameters1.ts, 19, 15))
>args : Symbol(args, Decl(signatureCombiningRestParameters1.ts, 19, 25))

((b: string) => void) &
>b : Symbol(b, Decl(signatureCombiningRestParameters1.ts, 20, 4))

((c: boolean) => void) = (arg) => {};
>c : Symbol(c, Decl(signatureCombiningRestParameters1.ts, 21, 4))
>arg : Symbol(arg, Decl(signatureCombiningRestParameters1.ts, 21, 28))

84 changes: 84 additions & 0 deletions tests/baselines/reference/signatureCombiningRestParameters1.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//// [tests/cases/compiler/signatureCombiningRestParameters1.ts] ////

=== signatureCombiningRestParameters1.ts ===
// https://github.com/microsoft/TypeScript/issues/58371

type T1 = "A" | "B";
>T1 : T1
> : ^^

type T2 = {
>T2 : T2
> : ^^

C: [string];
>C : [string]
> : ^^^^^^^^

D: [number];
>D : [number]
> : ^^^^^^^^

};

declare const map: {
>map : { A: () => unknown; B: () => unknown; C: (args_0: string) => unknown; D: (args_0: number) => unknown; }
> : ^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^

[K in T1 | keyof T2]: (...args: K extends keyof T2 ? T2[K] : []) => unknown;
>args : K extends keyof T2 ? T2[K] : []
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

};

declare const args: any;
>args : any
> : ^^^

for (const [key, fn] of Object.entries(map)) {
>key : string
> : ^^^^^^
>fn : (() => unknown) | (() => unknown) | ((args_0: string) => unknown) | ((args_0: number) => unknown)
> : ^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^
>Object.entries(map) : [string, (() => unknown) | (() => unknown) | ((args_0: string) => unknown) | ((args_0: number) => unknown)][]
> : ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
>Object.entries : { <T>(o: { [s: string]: T; } | ArrayLike<T>): [string, T][]; (o: {}): [string, any][]; }
> : ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^
>Object : ObjectConstructor
> : ^^^^^^^^^^^^^^^^^
>entries : { <T>(o: { [s: string]: T; } | ArrayLike<T>): [string, T][]; (o: {}): [string, any][]; }
> : ^^^ ^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^
>map : { A: () => unknown; B: () => unknown; C: (args_0: string) => unknown; D: (args_0: number) => unknown; }
> : ^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^

fn(...args);
>fn(...args) : unknown
> : ^^^^^^^
>fn : (() => unknown) | (() => unknown) | ((args_0: string) => unknown) | ((args_0: number) => unknown)
> : ^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^
>...args : any
> : ^^^
>args : any
> : ^^^
}

const test2: ((a: number, ...args: []) => void) &
>test2 : ((a: number) => void) & ((b: string) => void) & ((c: boolean) => void)
> : ^^ ^^ ^^^^^ ^^^^^^ ^^ ^^^^^ ^^^^^^ ^^ ^^^^^ ^
>a : number
> : ^^^^^^
>args : []
> : ^^

((b: string) => void) &
>b : string
> : ^^^^^^

((c: boolean) => void) = (arg) => {};
>c : boolean
> : ^^^^^^^
>(arg) => {} : (arg: string | number | boolean) => void
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>arg : string | number | boolean
> : ^^^^^^^^^^^^^^^^^^^^^^^^^

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//// [tests/cases/compiler/signatureCombiningRestParameters2.ts] ////

=== signatureCombiningRestParameters2.ts ===
interface Console {
>Console : Symbol(Console, Decl(lib.dom.d.ts, --, --), Decl(signatureCombiningRestParameters2.ts, 0, 0))

log(message?: any, ...optionalParams: any[]): void;
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --), Decl(signatureCombiningRestParameters2.ts, 0, 19))
>message : Symbol(message, Decl(signatureCombiningRestParameters2.ts, 1, 6))
>optionalParams : Symbol(optionalParams, Decl(signatureCombiningRestParameters2.ts, 1, 20))
}

let logs: string[] = [];
>logs : Symbol(logs, Decl(signatureCombiningRestParameters2.ts, 4, 3))

let originalLog: typeof console.log;
>originalLog : Symbol(originalLog, Decl(signatureCombiningRestParameters2.ts, 5, 3))
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --), Decl(signatureCombiningRestParameters2.ts, 0, 19))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --), Decl(signatureCombiningRestParameters2.ts, 0, 19))

console.log = (...args) => {
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --), Decl(signatureCombiningRestParameters2.ts, 0, 19))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --), Decl(signatureCombiningRestParameters2.ts, 0, 19))
>args : Symbol(args, Decl(signatureCombiningRestParameters2.ts, 6, 15))

logs.push(...args);
>logs.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>logs : Symbol(logs, Decl(signatureCombiningRestParameters2.ts, 4, 3))
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>args : Symbol(args, Decl(signatureCombiningRestParameters2.ts, 6, 15))

};

57 changes: 57 additions & 0 deletions tests/baselines/reference/signatureCombiningRestParameters2.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//// [tests/cases/compiler/signatureCombiningRestParameters2.ts] ////

=== signatureCombiningRestParameters2.ts ===
interface Console {
log(message?: any, ...optionalParams: any[]): void;
>log : { (...data: any[]): void; (message?: any, ...optionalParams: any[]): void; }
> : ^^^^^^ ^^ ^^^ ^^^ ^^^ ^^^^^ ^^ ^^^ ^^^
>message : any
>optionalParams : any[]
> : ^^^^^
}

let logs: string[] = [];
>logs : string[]
> : ^^^^^^^^
>[] : never[]
> : ^^^^^^^

let originalLog: typeof console.log;
>originalLog : { (...data: any[]): void; (message?: any, ...optionalParams: any[]): void; }
> : ^^^^^^ ^^ ^^^ ^^^ ^^^ ^^^^^ ^^ ^^^ ^^^
>console.log : { (...data: any[]): void; (message?: any, ...optionalParams: any[]): void; }
> : ^^^^^^ ^^ ^^^ ^^^ ^^^ ^^^^^ ^^ ^^^ ^^^
>console : Console
> : ^^^^^^^
>log : { (...data: any[]): void; (message?: any, ...optionalParams: any[]): void; }
> : ^^^^^^ ^^ ^^^ ^^^ ^^^ ^^^^^ ^^ ^^^ ^^^

console.log = (...args) => {
>console.log = (...args) => { logs.push(...args);} : (args_0?: any, ...args: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>console.log : { (...data: any[]): void; (message?: any, ...optionalParams: any[]): void; }
> : ^^^^^^ ^^ ^^^ ^^^ ^^^ ^^^^^ ^^ ^^^ ^^^
>console : Console
> : ^^^^^^^
>log : { (...data: any[]): void; (message?: any, ...optionalParams: any[]): void; }
> : ^^^^^^ ^^ ^^^ ^^^ ^^^ ^^^^^ ^^ ^^^ ^^^
>(...args) => { logs.push(...args);} : (args_0?: any, ...args: any[]) => void
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>args : [any?, ...any[]]
> : ^^^^^^^^^^^^^^^^

logs.push(...args);
>logs.push(...args) : number
> : ^^^^^^
>logs.push : (...items: string[]) => number
> : ^^^^ ^^^^^^^^^^^^^^^
>logs : string[]
> : ^^^^^^^^
>push : (...items: string[]) => number
> : ^^^^ ^^^^^^^^^^^^^^^
>...args : any
>args : [any?, ...any[]]
> : ^^^^^^^^^^^^^^^^

};

Loading

0 comments on commit 55886a1

Please sign in to comment.