Skip to content

Report generic rest parameters as unreliable variance positions #33020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13900,7 +13900,7 @@ namespace ts {
target: Signature,
ignoreReturnTypes: boolean): boolean {
return compareSignaturesRelated(source, target, CallbackCheck.None, ignoreReturnTypes, /*reportErrors*/ false,
/*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
/*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined) !== Ternary.False;
}

type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
Expand All @@ -13924,7 +13924,8 @@ namespace ts {
reportErrors: boolean,
errorReporter: ErrorReporter | undefined,
incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
compareTypes: TypeComparer): Ternary {
compareTypes: TypeComparer,
reportUnreliableMarkers: TypeMapper | undefined): Ternary {
// TODO (drosen): De-duplicate code between related functions.
if (source === target) {
return Ternary.True;
Expand All @@ -13947,6 +13948,9 @@ namespace ts {
const sourceCount = getParameterCount(source);
const sourceRestType = getNonArrayRestType(source);
const targetRestType = getNonArrayRestType(target);
if (sourceRestType || targetRestType) {
void instantiateType(sourceRestType || targetRestType, reportUnreliableMarkers);
}
if (sourceRestType && targetRestType && sourceCount !== targetCount) {
// We're not able to relate misaligned complex rest parameters
return Ternary.False;
Expand Down Expand Up @@ -13994,7 +13998,7 @@ namespace ts {
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
const related = callbacks ?
// TODO: GH#18217 It will work if they're both `undefined`, but not if only one is
compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, incompatibleErrorReporter, compareTypes) :
compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
!callbackCheck && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
if (!related) {
if (reportErrors) {
Expand Down Expand Up @@ -15948,7 +15952,7 @@ namespace ts {
*/
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, incompatibleReporter, isRelatedTo);
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
}

function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//// [aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts]
// the type printback for every `test` below should be "y"

type ExtendedMapper<HandledInputT, OutputT, ArgsT extends any[]> = (name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT;
type a = ExtendedMapper<any, any, [any]>;
type b = ExtendedMapper<any, any, any[]>;
type test = a extends b ? "y" : "n"
let check: test = "y";


type ExtendedMapper1<HandledInputT, OutputT, ArgsT extends any[]> = (
(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT
);

type a1 = ExtendedMapper1<any, any, [any]>;
type b1 = ExtendedMapper1<any, any, any[]>;
type test1 = a1 extends b1 ? "y" : "n"
let check1: test1 = "y";

type ExtendedMapper2<HandledInputT, OutputT, ArgsT extends any[]> = (
{x:(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT}["x"]
);

type a2 = ExtendedMapper2<any, any, [any]>;
type b2 = ExtendedMapper2<any, any, any[]>;
type test2 = a2 extends b2 ? "y" : "n"
let check2: test2 = "y";

type a3 = (name: string, mixed: any, args_0: any) => any
type b3 = (name: string, mixed: any, ...args: any[]) => any

type test3 = a3 extends b3 ? "y" : "n"
let check3: test3 = "y";


//// [aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.js]
"use strict";
// the type printback for every `test` below should be "y"
var check = "y";
var check1 = "y";
var check2 = "y";
var check3 = "y";
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
=== tests/cases/compiler/aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts ===
// the type printback for every `test` below should be "y"

type ExtendedMapper<HandledInputT, OutputT, ArgsT extends any[]> = (name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT;
>ExtendedMapper : Symbol(ExtendedMapper, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 0, 0))
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 20))
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 34))
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 43))
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 68))
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 82))
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 20))
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 105))
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 43))
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 34))

type a = ExtendedMapper<any, any, [any]>;
>a : Symbol(a, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 134))
>ExtendedMapper : Symbol(ExtendedMapper, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 0, 0))

type b = ExtendedMapper<any, any, any[]>;
>b : Symbol(b, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 3, 41))
>ExtendedMapper : Symbol(ExtendedMapper, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 0, 0))

type test = a extends b ? "y" : "n"
>test : Symbol(test, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 4, 41))
>a : Symbol(a, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 2, 134))
>b : Symbol(b, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 3, 41))

let check: test = "y";
>check : Symbol(check, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 3))
>test : Symbol(test, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 4, 41))


type ExtendedMapper1<HandledInputT, OutputT, ArgsT extends any[]> = (
>ExtendedMapper1 : Symbol(ExtendedMapper1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 22))
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 21))
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 35))
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 44))

(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 10, 5))
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 10, 19))
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 21))
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 10, 42))
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 44))
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 9, 35))

);

type a1 = ExtendedMapper1<any, any, [any]>;
>a1 : Symbol(a1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 11, 2))
>ExtendedMapper1 : Symbol(ExtendedMapper1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 22))

type b1 = ExtendedMapper1<any, any, any[]>;
>b1 : Symbol(b1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 13, 43))
>ExtendedMapper1 : Symbol(ExtendedMapper1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 6, 22))

type test1 = a1 extends b1 ? "y" : "n"
>test1 : Symbol(test1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 14, 43))
>a1 : Symbol(a1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 11, 2))
>b1 : Symbol(b1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 13, 43))

let check1: test1 = "y";
>check1 : Symbol(check1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 3))
>test1 : Symbol(test1, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 14, 43))

type ExtendedMapper2<HandledInputT, OutputT, ArgsT extends any[]> = (
>ExtendedMapper2 : Symbol(ExtendedMapper2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 24))
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 21))
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 35))
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 44))

{x:(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT}["x"]
>x : Symbol(x, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 5))
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 8))
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 22))
>HandledInputT : Symbol(HandledInputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 21))
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 19, 45))
>ArgsT : Symbol(ArgsT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 44))
>OutputT : Symbol(OutputT, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 18, 35))

);

type a2 = ExtendedMapper2<any, any, [any]>;
>a2 : Symbol(a2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 20, 2))
>ExtendedMapper2 : Symbol(ExtendedMapper2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 24))

type b2 = ExtendedMapper2<any, any, any[]>;
>b2 : Symbol(b2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 22, 43))
>ExtendedMapper2 : Symbol(ExtendedMapper2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 16, 24))

type test2 = a2 extends b2 ? "y" : "n"
>test2 : Symbol(test2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 23, 43))
>a2 : Symbol(a2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 20, 2))
>b2 : Symbol(b2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 22, 43))

let check2: test2 = "y";
>check2 : Symbol(check2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 25, 3))
>test2 : Symbol(test2, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 23, 43))

type a3 = (name: string, mixed: any, args_0: any) => any
>a3 : Symbol(a3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 25, 24))
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 11))
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 24))
>args_0 : Symbol(args_0, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 36))

type b3 = (name: string, mixed: any, ...args: any[]) => any
>b3 : Symbol(b3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 56))
>name : Symbol(name, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 11))
>mixed : Symbol(mixed, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 24))
>args : Symbol(args, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 36))

type test3 = a3 extends b3 ? "y" : "n"
>test3 : Symbol(test3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 59))
>a3 : Symbol(a3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 25, 24))
>b3 : Symbol(b3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 27, 56))

let check3: test3 = "y";
>check3 : Symbol(check3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 31, 3))
>test3 : Symbol(test3, Decl(aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts, 28, 59))

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
=== tests/cases/compiler/aliasOfGenericFunctionWithRestBehavedSameAsUnaliased.ts ===
// the type printback for every `test` below should be "y"

type ExtendedMapper<HandledInputT, OutputT, ArgsT extends any[]> = (name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT;
>ExtendedMapper : ExtendedMapper<HandledInputT, OutputT, ArgsT>
>name : string
>mixed : HandledInputT
>args : ArgsT

type a = ExtendedMapper<any, any, [any]>;
>a : ExtendedMapper<any, any, [any]>

type b = ExtendedMapper<any, any, any[]>;
>b : ExtendedMapper<any, any, any[]>

type test = a extends b ? "y" : "n"
>test : "y"

let check: test = "y";
>check : "y"
>"y" : "y"


type ExtendedMapper1<HandledInputT, OutputT, ArgsT extends any[]> = (
>ExtendedMapper1 : ExtendedMapper1<HandledInputT, OutputT, ArgsT>

(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT
>name : string
>mixed : HandledInputT
>args : ArgsT

);

type a1 = ExtendedMapper1<any, any, [any]>;
>a1 : ExtendedMapper1<any, any, [any]>

type b1 = ExtendedMapper1<any, any, any[]>;
>b1 : ExtendedMapper1<any, any, any[]>

type test1 = a1 extends b1 ? "y" : "n"
>test1 : "y"

let check1: test1 = "y";
>check1 : "y"
>"y" : "y"

type ExtendedMapper2<HandledInputT, OutputT, ArgsT extends any[]> = (
>ExtendedMapper2 : (name: string, mixed: HandledInputT, ...args: ArgsT) => OutputT

{x:(name : string, mixed : HandledInputT, ...args : ArgsT) => OutputT}["x"]
>x : (name: string, mixed: HandledInputT, ...args: ArgsT) => OutputT
>name : string
>mixed : HandledInputT
>args : ArgsT

);

type a2 = ExtendedMapper2<any, any, [any]>;
>a2 : (name: string, mixed: any, args_0: any) => any

type b2 = ExtendedMapper2<any, any, any[]>;
>b2 : (name: string, mixed: any, ...args: any[]) => any

type test2 = a2 extends b2 ? "y" : "n"
>test2 : "y"

let check2: test2 = "y";
>check2 : "y"
>"y" : "y"

type a3 = (name: string, mixed: any, args_0: any) => any
>a3 : a3
>name : string
>mixed : any
>args_0 : any

type b3 = (name: string, mixed: any, ...args: any[]) => any
>b3 : b3
>name : string
>mixed : any
>args : any[]

type test3 = a3 extends b3 ? "y" : "n"
>test3 : "y"

let check3: test3 = "y";
>check3 : "y"
>"y" : "y"

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//// [lambdaParameterWithTupleArgsHasCorrectAssignability.ts]
type MyTupleItem = {};
type MyTuple = [MyTupleItem, ...MyTupleItem[]];

type GenericFunction<T extends MyTuple> = (...fromArgs: T) => void;

class GenericClass<T extends MyTuple> {
from: GenericFunction<T> | undefined;
}

function createClass<T extends MyTuple>(f: GenericFunction<T>): GenericClass<T> {
return new GenericClass<T>(/* ... use f */);
}

function consumeClass(c: GenericClass<[string, boolean]>) { }

// should work
consumeClass(createClass(str => console.log(str.length)));

// should work
consumeClass(createClass((str, _unused_num) => console.log(str.length)));


//// [lambdaParameterWithTupleArgsHasCorrectAssignability.js]
"use strict";
var GenericClass = /** @class */ (function () {
function GenericClass() {
}
return GenericClass;
}());
function createClass(f) {
return new GenericClass( /* ... use f */);
}
function consumeClass(c) { }
// should work
consumeClass(createClass(function (str) { return console.log(str.length); }));
// should work
consumeClass(createClass(function (str, _unused_num) { return console.log(str.length); }));
Loading