diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 06fc5a6d396f0..e9ca3cc13ee66 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10424,6 +10424,24 @@ namespace ts { } function combineUnionParameters(left: Signature, right: Signature, mapper: TypeMapper | undefined) { + function isUncallableCallback(callbackType: Type): boolean { + const callbackSignatures = getSignaturesOfType(callbackType, SignatureKind.Call); + if (callbackSignatures.length === 0) { + return false; + } + return callbackSignatures.every(signature => { + for (let i = 0; i < getMinArgumentCount(signature); i++) { + const paramType = tryGetTypeAtPosition(signature, i); + if (!paramType) { + return false; + } + if (paramType.flags & TypeFlags.Never) { + return true; + } + } + return false; + }); + } const leftCount = getParameterCount(left); const rightCount = getParameterCount(right); const longest = leftCount >= rightCount ? left : right; @@ -10441,7 +10459,24 @@ namespace ts { if (shorter === right) { shorterParamType = instantiateType(shorterParamType, mapper); } - const unionParamType = getIntersectionType([longestParamType, shorterParamType]); + let unionParamType: Type | undefined; + if (isFunctionType(longestParamType) && isFunctionType(shorterParamType)) { + // If both parameters are callbacks, but only one of them is uncallable, + // use the type of the other one in the resulting union signature. + + // This improves inference around higher-order functions, like .map + // one a (T[] | never[]) value, since (never[]).map asks for a function + // (item: never, index: number) => R which it cannot actually call. + if (isUncallableCallback(longestParamType) && !isUncallableCallback(shorterParamType)) { + unionParamType = shorterParamType; + } + if (isUncallableCallback(shorterParamType) && !isUncallableCallback(longestParamType)) { + unionParamType = longestParamType; + } + } + if (!unionParamType) { + unionParamType = getIntersectionType([longestParamType, shorterParamType]); + } const isRestParam = eitherHasEffectiveRest && !needsExtraRestElement && i === (longestCount - 1); const isOptional = i >= getMinArgumentCount(longest) && i >= getMinArgumentCount(shorter); const leftName = i >= leftCount ? undefined : getParameterNameAtPosition(left, i); diff --git a/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.errors.txt b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.errors.txt new file mode 100644 index 0000000000000..58858495c7fc0 --- /dev/null +++ b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.errors.txt @@ -0,0 +1,60 @@ +tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts(24,5): error TS7006: Parameter 'a' implicitly has an 'any' type. +tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts(24,8): error TS7006: Parameter 'b' implicitly has an 'any' type. +tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts(39,5): error TS7006: Parameter 'a' implicitly has an 'any' type. +tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts(39,8): error TS7006: Parameter 'b' implicitly has an 'any' type. + + +==== tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts (4 errors) ==== + let f!: + | (( + callbackfnStr: (valueStr: string, indexStr: number, arrayStr: string[]) => UStr, + ) => UStr[]) + | (( + callbackfnNev: (valueNev: never, indexNev: number, arrayNev: never[]) => UNev, + ) => UNev[]); + + f(item => item.length); + + function orDefault(x: T | null, d: D): T | D { + return x !== null ? x : d; + } + + const xs: string[] | null = ["a", "bc", "def"]; + + const y = orDefault(xs, []).map(word => word.length); + + // Now, check optional arguments for correctness: + + + + const f1: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y?: never) => void) => void) = null as any; + f1((a, b) => { + ~ +!!! error TS7006: Parameter 'a' implicitly has an 'any' type. + ~ +!!! error TS7006: Parameter 'b' implicitly has an 'any' type. + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) + }) + + const f2: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y: never) => void) => void) = null as any; + f2((a, b) => { + // The 'remove uncallable candidates' rule applies: + a; // should be string + b; // should be number|undefined + }) + + + const f3: ((ask: (x: string, ...y: number[]) => void) => void) | ((ask: (x: number, ...y: never[]) => void) => void) = null as any; + f3((a, b) => { + ~ +!!! error TS7006: Parameter 'a' implicitly has an 'any' type. + ~ +!!! error TS7006: Parameter 'b' implicitly has an 'any' type. + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) + }) + + \ No newline at end of file diff --git a/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.js b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.js new file mode 100644 index 0000000000000..2609912ed50f9 --- /dev/null +++ b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.js @@ -0,0 +1,75 @@ +//// [resolveIncompatibleMethodCallbackWithNeverArgument.ts] +let f!: + | (( + callbackfnStr: (valueStr: string, indexStr: number, arrayStr: string[]) => UStr, + ) => UStr[]) + | (( + callbackfnNev: (valueNev: never, indexNev: number, arrayNev: never[]) => UNev, + ) => UNev[]); + +f(item => item.length); + +function orDefault(x: T | null, d: D): T | D { + return x !== null ? x : d; +} + +const xs: string[] | null = ["a", "bc", "def"]; + +const y = orDefault(xs, []).map(word => word.length); + +// Now, check optional arguments for correctness: + + + +const f1: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y?: never) => void) => void) = null as any; +f1((a, b) => { + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) +}) + +const f2: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y: never) => void) => void) = null as any; +f2((a, b) => { + // The 'remove uncallable candidates' rule applies: + a; // should be string + b; // should be number|undefined +}) + + +const f3: ((ask: (x: string, ...y: number[]) => void) => void) | ((ask: (x: number, ...y: never[]) => void) => void) = null as any; +f3((a, b) => { + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) +}) + + + +//// [resolveIncompatibleMethodCallbackWithNeverArgument.js] +"use strict"; +var f; +f(function (item) { return item.length; }); +function orDefault(x, d) { + return x !== null ? x : d; +} +var xs = ["a", "bc", "def"]; +var y = orDefault(xs, []).map(function (word) { return word.length; }); +// Now, check optional arguments for correctness: +var f1 = null; +f1(function (a, b) { + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) +}); +var f2 = null; +f2(function (a, b) { + // The 'remove uncallable candidates' rule applies: + a; // should be string + b; // should be number|undefined +}); +var f3 = null; +f3(function (a, b) { + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) +}); diff --git a/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.symbols b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.symbols new file mode 100644 index 0000000000000..f2885b567b118 --- /dev/null +++ b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.symbols @@ -0,0 +1,143 @@ +=== tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts === +let f!: +>f : Symbol(f, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 0, 3)) + + | (( +>UStr : Symbol(UStr, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 1, 6)) + + callbackfnStr: (valueStr: string, indexStr: number, arrayStr: string[]) => UStr, +>callbackfnStr : Symbol(callbackfnStr, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 1, 12)) +>valueStr : Symbol(valueStr, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 2, 20)) +>indexStr : Symbol(indexStr, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 2, 37)) +>arrayStr : Symbol(arrayStr, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 2, 55)) +>UStr : Symbol(UStr, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 1, 6)) + + ) => UStr[]) +>UStr : Symbol(UStr, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 1, 6)) + + | (( +>UNev : Symbol(UNev, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 4, 6)) + + callbackfnNev: (valueNev: never, indexNev: number, arrayNev: never[]) => UNev, +>callbackfnNev : Symbol(callbackfnNev, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 4, 12)) +>valueNev : Symbol(valueNev, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 5, 20)) +>indexNev : Symbol(indexNev, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 5, 36)) +>arrayNev : Symbol(arrayNev, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 5, 54)) +>UNev : Symbol(UNev, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 4, 6)) + + ) => UNev[]); +>UNev : Symbol(UNev, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 4, 6)) + +f(item => item.length); +>f : Symbol(f, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 0, 3)) +>item : Symbol(item, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 8, 2)) +>item.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>item : Symbol(item, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 8, 2)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + +function orDefault(x: T | null, d: D): T | D { +>orDefault : Symbol(orDefault, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 8, 23)) +>T : Symbol(T, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 19)) +>D : Symbol(D, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 21)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 25)) +>T : Symbol(T, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 19)) +>d : Symbol(d, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 37)) +>D : Symbol(D, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 21)) +>T : Symbol(T, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 19)) +>D : Symbol(D, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 21)) + + return x !== null ? x : d; +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 25)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 25)) +>d : Symbol(d, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 10, 37)) +} + +const xs: string[] | null = ["a", "bc", "def"]; +>xs : Symbol(xs, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 14, 5)) + +const y = orDefault(xs, []).map(word => word.length); +>y : Symbol(y, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 16, 5)) +>orDefault(xs, []).map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>orDefault : Symbol(orDefault, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 8, 23)) +>xs : Symbol(xs, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 14, 5)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>word : Symbol(word, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 16, 32)) +>word.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>word : Symbol(word, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 16, 32)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + +// Now, check optional arguments for correctness: + + + +const f1: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y?: never) => void) => void) = null as any; +>f1 : Symbol(f1, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 5)) +>ask : Symbol(ask, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 12)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 18)) +>y : Symbol(y, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 28)) +>ask : Symbol(ask, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 63)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 69)) +>y : Symbol(y, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 79)) + +f1((a, b) => { +>f1 : Symbol(f1, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 22, 5)) +>a : Symbol(a, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 23, 4)) +>b : Symbol(b, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 23, 6)) + + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) +>a : Symbol(a, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 23, 4)) + + b; // should be 'any' (in future, could be number|undefined) +>b : Symbol(b, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 23, 6)) + +}) + +const f2: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y: never) => void) => void) = null as any; +>f2 : Symbol(f2, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 5)) +>ask : Symbol(ask, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 12)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 18)) +>y : Symbol(y, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 28)) +>ask : Symbol(ask, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 63)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 69)) +>y : Symbol(y, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 79)) + +f2((a, b) => { +>f2 : Symbol(f2, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 29, 5)) +>a : Symbol(a, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 30, 4)) +>b : Symbol(b, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 30, 6)) + + // The 'remove uncallable candidates' rule applies: + a; // should be string +>a : Symbol(a, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 30, 4)) + + b; // should be number|undefined +>b : Symbol(b, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 30, 6)) + +}) + + +const f3: ((ask: (x: string, ...y: number[]) => void) => void) | ((ask: (x: number, ...y: never[]) => void) => void) = null as any; +>f3 : Symbol(f3, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 5)) +>ask : Symbol(ask, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 12)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 18)) +>y : Symbol(y, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 28)) +>ask : Symbol(ask, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 67)) +>x : Symbol(x, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 73)) +>y : Symbol(y, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 83)) + +f3((a, b) => { +>f3 : Symbol(f3, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 37, 5)) +>a : Symbol(a, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 38, 4)) +>b : Symbol(b, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 38, 6)) + + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) +>a : Symbol(a, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 38, 4)) + + b; // should be 'any' (in future, could be number|undefined) +>b : Symbol(b, Decl(resolveIncompatibleMethodCallbackWithNeverArgument.ts, 38, 6)) + +}) + + diff --git a/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.types b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.types new file mode 100644 index 0000000000000..3737c1f07e227 --- /dev/null +++ b/tests/baselines/reference/resolveIncompatibleMethodCallbackWithNeverArgument.types @@ -0,0 +1,155 @@ +=== tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts === +let f!: +>f : ((callbackfnStr: (valueStr: string, indexStr: number, arrayStr: string[]) => UStr) => UStr[]) | ((callbackfnNev: (valueNev: never, indexNev: number, arrayNev: never[]) => UNev) => UNev[]) + + | (( + callbackfnStr: (valueStr: string, indexStr: number, arrayStr: string[]) => UStr, +>callbackfnStr : (valueStr: string, indexStr: number, arrayStr: string[]) => UStr +>valueStr : string +>indexStr : number +>arrayStr : string[] + + ) => UStr[]) + | (( + callbackfnNev: (valueNev: never, indexNev: number, arrayNev: never[]) => UNev, +>callbackfnNev : (valueNev: never, indexNev: number, arrayNev: never[]) => UNev +>valueNev : never +>indexNev : number +>arrayNev : never[] + + ) => UNev[]); + +f(item => item.length); +>f(item => item.length) : number[] +>f : ((callbackfnStr: (valueStr: string, indexStr: number, arrayStr: string[]) => UStr) => UStr[]) | ((callbackfnNev: (valueNev: never, indexNev: number, arrayNev: never[]) => UNev) => UNev[]) +>item => item.length : (item: string) => number +>item : string +>item.length : number +>item : string +>length : number + +function orDefault(x: T | null, d: D): T | D { +>orDefault : (x: T | null, d: D) => T | D +>x : T | null +>null : null +>d : D + + return x !== null ? x : d; +>x !== null ? x : d : T | D +>x !== null : boolean +>x : T | null +>null : null +>x : T +>d : D +} + +const xs: string[] | null = ["a", "bc", "def"]; +>xs : string[] | null +>null : null +>["a", "bc", "def"] : string[] +>"a" : "a" +>"bc" : "bc" +>"def" : "def" + +const y = orDefault(xs, []).map(word => word.length); +>y : number[] +>orDefault(xs, []).map(word => word.length) : number[] +>orDefault(xs, []).map : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[]) +>orDefault(xs, []) : string[] | never[] +>orDefault : (x: T | null, d: D) => T | D +>xs : string[] +>[] : never[] +>map : ((callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]) | ((callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[]) +>word => word.length : (word: string) => number +>word : string +>word.length : number +>word : string +>length : number + +// Now, check optional arguments for correctness: + + + +const f1: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y?: never) => void) => void) = null as any; +>f1 : ((ask: (x: string, y?: number | undefined) => void) => void) | ((ask: (x: number, y?: undefined) => void) => void) +>ask : (x: string, y?: number | undefined) => void +>x : string +>y : number | undefined +>ask : (x: number, y?: undefined) => void +>x : number +>y : undefined +>null as any : any +>null : null + +f1((a, b) => { +>f1((a, b) => { // The 'remove uncallable candidates' rule DOES NOT APPLY: a; // should be 'any' (in future, could be string|number) b; // should be 'any' (in future, could be number|undefined)}) : void +>f1 : ((ask: (x: string, y?: number | undefined) => void) => void) | ((ask: (x: number, y?: undefined) => void) => void) +>(a, b) => { // The 'remove uncallable candidates' rule DOES NOT APPLY: a; // should be 'any' (in future, could be string|number) b; // should be 'any' (in future, could be number|undefined)} : (a: any, b: any) => void +>a : any +>b : any + + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) +>a : any + + b; // should be 'any' (in future, could be number|undefined) +>b : any + +}) + +const f2: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y: never) => void) => void) = null as any; +>f2 : ((ask: (x: string, y?: number | undefined) => void) => void) | ((ask: (x: number, y: never) => void) => void) +>ask : (x: string, y?: number | undefined) => void +>x : string +>y : number | undefined +>ask : (x: number, y: never) => void +>x : number +>y : never +>null as any : any +>null : null + +f2((a, b) => { +>f2((a, b) => { // The 'remove uncallable candidates' rule applies: a; // should be string b; // should be number|undefined}) : void +>f2 : ((ask: (x: string, y?: number | undefined) => void) => void) | ((ask: (x: number, y: never) => void) => void) +>(a, b) => { // The 'remove uncallable candidates' rule applies: a; // should be string b; // should be number|undefined} : (a: string, b: number | undefined) => void +>a : string +>b : number | undefined + + // The 'remove uncallable candidates' rule applies: + a; // should be string +>a : string + + b; // should be number|undefined +>b : number | undefined + +}) + + +const f3: ((ask: (x: string, ...y: number[]) => void) => void) | ((ask: (x: number, ...y: never[]) => void) => void) = null as any; +>f3 : ((ask: (x: string, ...y: number[]) => void) => void) | ((ask: (x: number, ...y: never[]) => void) => void) +>ask : (x: string, ...y: number[]) => void +>x : string +>y : number[] +>ask : (x: number, ...y: never[]) => void +>x : number +>y : never[] +>null as any : any +>null : null + +f3((a, b) => { +>f3((a, b) => { // The 'remove uncallable candidates' rule DOES NOT APPLY: a; // should be 'any' (in future, could be string|number) b; // should be 'any' (in future, could be number|undefined)}) : void +>f3 : ((ask: (x: string, ...y: number[]) => void) => void) | ((ask: (x: number, ...y: never[]) => void) => void) +>(a, b) => { // The 'remove uncallable candidates' rule DOES NOT APPLY: a; // should be 'any' (in future, could be string|number) b; // should be 'any' (in future, could be number|undefined)} : (a: any, b: any) => void +>a : any +>b : any + + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) +>a : any + + b; // should be 'any' (in future, could be number|undefined) +>b : any + +}) + + diff --git a/tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts b/tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts new file mode 100644 index 0000000000000..ccdf6ceb3cbd2 --- /dev/null +++ b/tests/cases/compiler/resolveIncompatibleMethodCallbackWithNeverArgument.ts @@ -0,0 +1,46 @@ +// @strict: true + +let f!: + | (( + callbackfnStr: (valueStr: string, indexStr: number, arrayStr: string[]) => UStr, + ) => UStr[]) + | (( + callbackfnNev: (valueNev: never, indexNev: number, arrayNev: never[]) => UNev, + ) => UNev[]); + +f(item => item.length); + +function orDefault(x: T | null, d: D): T | D { + return x !== null ? x : d; +} + +const xs: string[] | null = ["a", "bc", "def"]; + +const y = orDefault(xs, []).map(word => word.length); + +// Now, check optional arguments for correctness: + + + +const f1: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y?: never) => void) => void) = null as any; +f1((a, b) => { + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) +}) + +const f2: ((ask: (x: string, y?: number) => void) => void) | ((ask: (x: number, y: never) => void) => void) = null as any; +f2((a, b) => { + // The 'remove uncallable candidates' rule applies: + a; // should be string + b; // should be number|undefined +}) + + +const f3: ((ask: (x: string, ...y: number[]) => void) => void) | ((ask: (x: number, ...y: never[]) => void) => void) = null as any; +f3((a, b) => { + // The 'remove uncallable candidates' rule DOES NOT APPLY: + a; // should be 'any' (in future, could be string|number) + b; // should be 'any' (in future, could be number|undefined) +}) +