Improve logic that chooses co- vs. contra-variant inferences (#52123)

This commit is contained in:
Anders Hejlsberg
2023-01-06 05:56:51 -10:00
committed by GitHub
parent 4b52d3a61a
commit fc8538608b
5 changed files with 888 additions and 5 deletions

View File

@@ -24504,11 +24504,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (signature) {
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
if (inference.contraCandidates) {
// If we have both co- and contra-variant inferences, we prefer the contra-variant inference
// unless the co-variant inference is a subtype of some contra-variant inference and not 'never'.
inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) ?
inferredCovariantType : getContravariantInference(inference);
// If we have both co- and contra-variant inferences, we use the co-variant inference if it is not 'never',
// it is a subtype of some contra-variant inference, and no other type parameter is constrained to this type
// parameter and has inferences that would conflict. Otherwise, we use the contra-variant inference.
const useCovariantType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) &&
every(context.inferences, other => other === inference ||
getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter ||
every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType)));
inferredType = useCovariantType ? inferredCovariantType : getContravariantInference(inference);
}
else if (inferredCovariantType) {
inferredType = inferredCovariantType;

View File

@@ -0,0 +1,140 @@
//// [coAndContraVariantInferences2.ts]
interface A { a: string }
interface B extends A { b: string }
interface C extends A { c: string }
declare function cast<T, U extends T>(x: T, test: (x: T) => x is U): U;
declare function isC(x: A): x is C;
function f1(a: A, b: B) {
const x1 = cast(a, isC); // cast<A, C>
const x2 = cast(b, isC); // cast<A, C>
}
declare function useA(a: A): void;
declare function consume<T, U extends T>(t: T, u: U, f: (x: T) => void): void;
function f2(b: B, c: C) {
consume(b, c, useA); // consume<A, C>
consume(c, b, useA); // consume<A, B>
consume(b, b, useA); // consume<B, B>
consume(c, c, useA); // consume<C, C>
}
// Repro from #52111
enum SyntaxKind {
Block,
Identifier,
CaseClause,
FunctionExpression,
FunctionDeclaration,
}
interface Node { kind: SyntaxKind; }
interface Expression extends Node { _expressionBrand: any; }
interface Declaration extends Node { _declarationBrand: any; }
interface Block extends Node { kind: SyntaxKind.Block; }
interface Identifier extends Expression, Declaration { kind: SyntaxKind.Identifier; }
interface CaseClause extends Node { kind: SyntaxKind.CaseClause; }
interface FunctionDeclaration extends Declaration { kind: SyntaxKind.FunctionDeclaration; }
type HasLocals = Block | FunctionDeclaration;
declare function canHaveLocals(node: Node): node is HasLocals;
declare function assertNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U;
declare function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined): void;
function foo(node: FunctionDeclaration | CaseClause) {
assertNode(node, canHaveLocals); // assertNode<Node, HasLocals>
node; // FunctionDeclaration
}
declare function isExpression(node: Node): node is Expression;
declare function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut;
function bar(node: Identifier | FunctionDeclaration) {
const a = tryCast(node, isExpression); // tryCast<Expression, Node>
}
// Repro from #49924
const enum SyntaxKind1 {
ClassExpression,
ClassStatement,
}
interface Node1 {
kind: SyntaxKind1;
}
interface Statement1 extends Node1 {
_statementBrand: any;
}
interface ClassExpression1 extends Node1 {
kind: SyntaxKind1.ClassExpression;
}
interface ClassStatement1 extends Statement1 {
kind: SyntaxKind1.ClassStatement;
}
type ClassLike1 = ClassExpression1 | ClassStatement1;
declare function isClassLike(node: Node1): node is ClassLike1;
declare const statement: Statement1 | undefined;
const maybeClassStatement = tryCast(statement, isClassLike); // ClassLike1
// Repro from #49924
interface TypeNode extends Node {
typeInfo: string;
}
interface NodeArray<T extends Node> extends Array<T> {
someProp: string;
}
declare function isNodeArray<T extends Node>(array: readonly T[]): array is NodeArray<T>;
declare const types: readonly TypeNode[];
const x = tryCast(types, isNodeArray); // NodeAray<TypeNode>
//// [coAndContraVariantInferences2.js]
"use strict";
function f1(a, b) {
var x1 = cast(a, isC); // cast<A, C>
var x2 = cast(b, isC); // cast<A, C>
}
function f2(b, c) {
consume(b, c, useA); // consume<A, C>
consume(c, b, useA); // consume<A, B>
consume(b, b, useA); // consume<B, B>
consume(c, c, useA); // consume<C, C>
}
// Repro from #52111
var SyntaxKind;
(function (SyntaxKind) {
SyntaxKind[SyntaxKind["Block"] = 0] = "Block";
SyntaxKind[SyntaxKind["Identifier"] = 1] = "Identifier";
SyntaxKind[SyntaxKind["CaseClause"] = 2] = "CaseClause";
SyntaxKind[SyntaxKind["FunctionExpression"] = 3] = "FunctionExpression";
SyntaxKind[SyntaxKind["FunctionDeclaration"] = 4] = "FunctionDeclaration";
})(SyntaxKind || (SyntaxKind = {}));
function foo(node) {
assertNode(node, canHaveLocals); // assertNode<Node, HasLocals>
node; // FunctionDeclaration
}
function bar(node) {
var a = tryCast(node, isExpression); // tryCast<Expression, Node>
}
var maybeClassStatement = tryCast(statement, isClassLike); // ClassLike1
var x = tryCast(types, isNodeArray); // NodeAray<TypeNode>

View File

@@ -0,0 +1,367 @@
=== tests/cases/compiler/coAndContraVariantInferences2.ts ===
interface A { a: string }
>A : Symbol(A, Decl(coAndContraVariantInferences2.ts, 0, 0))
>a : Symbol(A.a, Decl(coAndContraVariantInferences2.ts, 0, 13))
interface B extends A { b: string }
>B : Symbol(B, Decl(coAndContraVariantInferences2.ts, 0, 25))
>A : Symbol(A, Decl(coAndContraVariantInferences2.ts, 0, 0))
>b : Symbol(B.b, Decl(coAndContraVariantInferences2.ts, 1, 23))
interface C extends A { c: string }
>C : Symbol(C, Decl(coAndContraVariantInferences2.ts, 1, 35))
>A : Symbol(A, Decl(coAndContraVariantInferences2.ts, 0, 0))
>c : Symbol(C.c, Decl(coAndContraVariantInferences2.ts, 2, 23))
declare function cast<T, U extends T>(x: T, test: (x: T) => x is U): U;
>cast : Symbol(cast, Decl(coAndContraVariantInferences2.ts, 2, 35))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 4, 22))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 4, 24))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 4, 22))
>x : Symbol(x, Decl(coAndContraVariantInferences2.ts, 4, 38))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 4, 22))
>test : Symbol(test, Decl(coAndContraVariantInferences2.ts, 4, 43))
>x : Symbol(x, Decl(coAndContraVariantInferences2.ts, 4, 51))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 4, 22))
>x : Symbol(x, Decl(coAndContraVariantInferences2.ts, 4, 51))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 4, 24))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 4, 24))
declare function isC(x: A): x is C;
>isC : Symbol(isC, Decl(coAndContraVariantInferences2.ts, 4, 71))
>x : Symbol(x, Decl(coAndContraVariantInferences2.ts, 6, 21))
>A : Symbol(A, Decl(coAndContraVariantInferences2.ts, 0, 0))
>x : Symbol(x, Decl(coAndContraVariantInferences2.ts, 6, 21))
>C : Symbol(C, Decl(coAndContraVariantInferences2.ts, 1, 35))
function f1(a: A, b: B) {
>f1 : Symbol(f1, Decl(coAndContraVariantInferences2.ts, 6, 35))
>a : Symbol(a, Decl(coAndContraVariantInferences2.ts, 8, 12))
>A : Symbol(A, Decl(coAndContraVariantInferences2.ts, 0, 0))
>b : Symbol(b, Decl(coAndContraVariantInferences2.ts, 8, 17))
>B : Symbol(B, Decl(coAndContraVariantInferences2.ts, 0, 25))
const x1 = cast(a, isC); // cast<A, C>
>x1 : Symbol(x1, Decl(coAndContraVariantInferences2.ts, 9, 9))
>cast : Symbol(cast, Decl(coAndContraVariantInferences2.ts, 2, 35))
>a : Symbol(a, Decl(coAndContraVariantInferences2.ts, 8, 12))
>isC : Symbol(isC, Decl(coAndContraVariantInferences2.ts, 4, 71))
const x2 = cast(b, isC); // cast<A, C>
>x2 : Symbol(x2, Decl(coAndContraVariantInferences2.ts, 10, 9))
>cast : Symbol(cast, Decl(coAndContraVariantInferences2.ts, 2, 35))
>b : Symbol(b, Decl(coAndContraVariantInferences2.ts, 8, 17))
>isC : Symbol(isC, Decl(coAndContraVariantInferences2.ts, 4, 71))
}
declare function useA(a: A): void;
>useA : Symbol(useA, Decl(coAndContraVariantInferences2.ts, 11, 1))
>a : Symbol(a, Decl(coAndContraVariantInferences2.ts, 13, 22))
>A : Symbol(A, Decl(coAndContraVariantInferences2.ts, 0, 0))
declare function consume<T, U extends T>(t: T, u: U, f: (x: T) => void): void;
>consume : Symbol(consume, Decl(coAndContraVariantInferences2.ts, 13, 34))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 15, 25))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 15, 27))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 15, 25))
>t : Symbol(t, Decl(coAndContraVariantInferences2.ts, 15, 41))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 15, 25))
>u : Symbol(u, Decl(coAndContraVariantInferences2.ts, 15, 46))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 15, 27))
>f : Symbol(f, Decl(coAndContraVariantInferences2.ts, 15, 52))
>x : Symbol(x, Decl(coAndContraVariantInferences2.ts, 15, 57))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 15, 25))
function f2(b: B, c: C) {
>f2 : Symbol(f2, Decl(coAndContraVariantInferences2.ts, 15, 78))
>b : Symbol(b, Decl(coAndContraVariantInferences2.ts, 17, 12))
>B : Symbol(B, Decl(coAndContraVariantInferences2.ts, 0, 25))
>c : Symbol(c, Decl(coAndContraVariantInferences2.ts, 17, 17))
>C : Symbol(C, Decl(coAndContraVariantInferences2.ts, 1, 35))
consume(b, c, useA); // consume<A, C>
>consume : Symbol(consume, Decl(coAndContraVariantInferences2.ts, 13, 34))
>b : Symbol(b, Decl(coAndContraVariantInferences2.ts, 17, 12))
>c : Symbol(c, Decl(coAndContraVariantInferences2.ts, 17, 17))
>useA : Symbol(useA, Decl(coAndContraVariantInferences2.ts, 11, 1))
consume(c, b, useA); // consume<A, B>
>consume : Symbol(consume, Decl(coAndContraVariantInferences2.ts, 13, 34))
>c : Symbol(c, Decl(coAndContraVariantInferences2.ts, 17, 17))
>b : Symbol(b, Decl(coAndContraVariantInferences2.ts, 17, 12))
>useA : Symbol(useA, Decl(coAndContraVariantInferences2.ts, 11, 1))
consume(b, b, useA); // consume<B, B>
>consume : Symbol(consume, Decl(coAndContraVariantInferences2.ts, 13, 34))
>b : Symbol(b, Decl(coAndContraVariantInferences2.ts, 17, 12))
>b : Symbol(b, Decl(coAndContraVariantInferences2.ts, 17, 12))
>useA : Symbol(useA, Decl(coAndContraVariantInferences2.ts, 11, 1))
consume(c, c, useA); // consume<C, C>
>consume : Symbol(consume, Decl(coAndContraVariantInferences2.ts, 13, 34))
>c : Symbol(c, Decl(coAndContraVariantInferences2.ts, 17, 17))
>c : Symbol(c, Decl(coAndContraVariantInferences2.ts, 17, 17))
>useA : Symbol(useA, Decl(coAndContraVariantInferences2.ts, 11, 1))
}
// Repro from #52111
enum SyntaxKind {
>SyntaxKind : Symbol(SyntaxKind, Decl(coAndContraVariantInferences2.ts, 22, 1))
Block,
>Block : Symbol(SyntaxKind.Block, Decl(coAndContraVariantInferences2.ts, 26, 17))
Identifier,
>Identifier : Symbol(SyntaxKind.Identifier, Decl(coAndContraVariantInferences2.ts, 27, 10))
CaseClause,
>CaseClause : Symbol(SyntaxKind.CaseClause, Decl(coAndContraVariantInferences2.ts, 28, 15))
FunctionExpression,
>FunctionExpression : Symbol(SyntaxKind.FunctionExpression, Decl(coAndContraVariantInferences2.ts, 29, 15))
FunctionDeclaration,
>FunctionDeclaration : Symbol(SyntaxKind.FunctionDeclaration, Decl(coAndContraVariantInferences2.ts, 30, 23))
}
interface Node { kind: SyntaxKind; }
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>kind : Symbol(Node.kind, Decl(coAndContraVariantInferences2.ts, 34, 16))
>SyntaxKind : Symbol(SyntaxKind, Decl(coAndContraVariantInferences2.ts, 22, 1))
interface Expression extends Node { _expressionBrand: any; }
>Expression : Symbol(Expression, Decl(coAndContraVariantInferences2.ts, 34, 36))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>_expressionBrand : Symbol(Expression._expressionBrand, Decl(coAndContraVariantInferences2.ts, 35, 35))
interface Declaration extends Node { _declarationBrand: any; }
>Declaration : Symbol(Declaration, Decl(coAndContraVariantInferences2.ts, 35, 60))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>_declarationBrand : Symbol(Declaration._declarationBrand, Decl(coAndContraVariantInferences2.ts, 36, 36))
interface Block extends Node { kind: SyntaxKind.Block; }
>Block : Symbol(Block, Decl(coAndContraVariantInferences2.ts, 36, 62))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>kind : Symbol(Block.kind, Decl(coAndContraVariantInferences2.ts, 37, 30))
>SyntaxKind : Symbol(SyntaxKind, Decl(coAndContraVariantInferences2.ts, 22, 1))
>Block : Symbol(SyntaxKind.Block, Decl(coAndContraVariantInferences2.ts, 26, 17))
interface Identifier extends Expression, Declaration { kind: SyntaxKind.Identifier; }
>Identifier : Symbol(Identifier, Decl(coAndContraVariantInferences2.ts, 37, 56))
>Expression : Symbol(Expression, Decl(coAndContraVariantInferences2.ts, 34, 36))
>Declaration : Symbol(Declaration, Decl(coAndContraVariantInferences2.ts, 35, 60))
>kind : Symbol(Identifier.kind, Decl(coAndContraVariantInferences2.ts, 38, 54))
>SyntaxKind : Symbol(SyntaxKind, Decl(coAndContraVariantInferences2.ts, 22, 1))
>Identifier : Symbol(SyntaxKind.Identifier, Decl(coAndContraVariantInferences2.ts, 27, 10))
interface CaseClause extends Node { kind: SyntaxKind.CaseClause; }
>CaseClause : Symbol(CaseClause, Decl(coAndContraVariantInferences2.ts, 38, 85))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>kind : Symbol(CaseClause.kind, Decl(coAndContraVariantInferences2.ts, 39, 35))
>SyntaxKind : Symbol(SyntaxKind, Decl(coAndContraVariantInferences2.ts, 22, 1))
>CaseClause : Symbol(SyntaxKind.CaseClause, Decl(coAndContraVariantInferences2.ts, 28, 15))
interface FunctionDeclaration extends Declaration { kind: SyntaxKind.FunctionDeclaration; }
>FunctionDeclaration : Symbol(FunctionDeclaration, Decl(coAndContraVariantInferences2.ts, 39, 66))
>Declaration : Symbol(Declaration, Decl(coAndContraVariantInferences2.ts, 35, 60))
>kind : Symbol(FunctionDeclaration.kind, Decl(coAndContraVariantInferences2.ts, 40, 51))
>SyntaxKind : Symbol(SyntaxKind, Decl(coAndContraVariantInferences2.ts, 22, 1))
>FunctionDeclaration : Symbol(SyntaxKind.FunctionDeclaration, Decl(coAndContraVariantInferences2.ts, 30, 23))
type HasLocals = Block | FunctionDeclaration;
>HasLocals : Symbol(HasLocals, Decl(coAndContraVariantInferences2.ts, 40, 91))
>Block : Symbol(Block, Decl(coAndContraVariantInferences2.ts, 36, 62))
>FunctionDeclaration : Symbol(FunctionDeclaration, Decl(coAndContraVariantInferences2.ts, 39, 66))
declare function canHaveLocals(node: Node): node is HasLocals;
>canHaveLocals : Symbol(canHaveLocals, Decl(coAndContraVariantInferences2.ts, 42, 45))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 43, 31))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 43, 31))
>HasLocals : Symbol(HasLocals, Decl(coAndContraVariantInferences2.ts, 40, 91))
declare function assertNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U;
>assertNode : Symbol(assertNode, Decl(coAndContraVariantInferences2.ts, 43, 62), Decl(coAndContraVariantInferences2.ts, 45, 127))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 45, 28))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 45, 43))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 45, 28))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 45, 57))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 45, 28))
>test : Symbol(test, Decl(coAndContraVariantInferences2.ts, 45, 77))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 45, 85))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 45, 28))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 45, 85))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 45, 43))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 45, 57))
>U : Symbol(U, Decl(coAndContraVariantInferences2.ts, 45, 43))
declare function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined): void;
>assertNode : Symbol(assertNode, Decl(coAndContraVariantInferences2.ts, 43, 62), Decl(coAndContraVariantInferences2.ts, 45, 127))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 46, 28))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>test : Symbol(test, Decl(coAndContraVariantInferences2.ts, 46, 51))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 46, 60))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
function foo(node: FunctionDeclaration | CaseClause) {
>foo : Symbol(foo, Decl(coAndContraVariantInferences2.ts, 46, 103))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 48, 13))
>FunctionDeclaration : Symbol(FunctionDeclaration, Decl(coAndContraVariantInferences2.ts, 39, 66))
>CaseClause : Symbol(CaseClause, Decl(coAndContraVariantInferences2.ts, 38, 85))
assertNode(node, canHaveLocals); // assertNode<Node, HasLocals>
>assertNode : Symbol(assertNode, Decl(coAndContraVariantInferences2.ts, 43, 62), Decl(coAndContraVariantInferences2.ts, 45, 127))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 48, 13))
>canHaveLocals : Symbol(canHaveLocals, Decl(coAndContraVariantInferences2.ts, 42, 45))
node; // FunctionDeclaration
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 48, 13))
}
declare function isExpression(node: Node): node is Expression;
>isExpression : Symbol(isExpression, Decl(coAndContraVariantInferences2.ts, 51, 1))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 53, 30))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 53, 30))
>Expression : Symbol(Expression, Decl(coAndContraVariantInferences2.ts, 34, 36))
declare function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut;
>tryCast : Symbol(tryCast, Decl(coAndContraVariantInferences2.ts, 53, 62))
>TOut : Symbol(TOut, Decl(coAndContraVariantInferences2.ts, 55, 25))
>TIn : Symbol(TIn, Decl(coAndContraVariantInferences2.ts, 55, 42))
>TIn : Symbol(TIn, Decl(coAndContraVariantInferences2.ts, 55, 42))
>value : Symbol(value, Decl(coAndContraVariantInferences2.ts, 55, 54))
>TIn : Symbol(TIn, Decl(coAndContraVariantInferences2.ts, 55, 42))
>test : Symbol(test, Decl(coAndContraVariantInferences2.ts, 55, 77))
>value : Symbol(value, Decl(coAndContraVariantInferences2.ts, 55, 85))
>TIn : Symbol(TIn, Decl(coAndContraVariantInferences2.ts, 55, 42))
>value : Symbol(value, Decl(coAndContraVariantInferences2.ts, 55, 85))
>TOut : Symbol(TOut, Decl(coAndContraVariantInferences2.ts, 55, 25))
>TOut : Symbol(TOut, Decl(coAndContraVariantInferences2.ts, 55, 25))
function bar(node: Identifier | FunctionDeclaration) {
>bar : Symbol(bar, Decl(coAndContraVariantInferences2.ts, 55, 121))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 57, 13))
>Identifier : Symbol(Identifier, Decl(coAndContraVariantInferences2.ts, 37, 56))
>FunctionDeclaration : Symbol(FunctionDeclaration, Decl(coAndContraVariantInferences2.ts, 39, 66))
const a = tryCast(node, isExpression); // tryCast<Expression, Node>
>a : Symbol(a, Decl(coAndContraVariantInferences2.ts, 58, 9))
>tryCast : Symbol(tryCast, Decl(coAndContraVariantInferences2.ts, 53, 62))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 57, 13))
>isExpression : Symbol(isExpression, Decl(coAndContraVariantInferences2.ts, 51, 1))
}
// Repro from #49924
const enum SyntaxKind1 {
>SyntaxKind1 : Symbol(SyntaxKind1, Decl(coAndContraVariantInferences2.ts, 59, 1))
ClassExpression,
>ClassExpression : Symbol(SyntaxKind1.ClassExpression, Decl(coAndContraVariantInferences2.ts, 63, 24))
ClassStatement,
>ClassStatement : Symbol(SyntaxKind1.ClassStatement, Decl(coAndContraVariantInferences2.ts, 64, 20))
}
interface Node1 {
>Node1 : Symbol(Node1, Decl(coAndContraVariantInferences2.ts, 66, 1))
kind: SyntaxKind1;
>kind : Symbol(Node1.kind, Decl(coAndContraVariantInferences2.ts, 68, 17))
>SyntaxKind1 : Symbol(SyntaxKind1, Decl(coAndContraVariantInferences2.ts, 59, 1))
}
interface Statement1 extends Node1 {
>Statement1 : Symbol(Statement1, Decl(coAndContraVariantInferences2.ts, 70, 1))
>Node1 : Symbol(Node1, Decl(coAndContraVariantInferences2.ts, 66, 1))
_statementBrand: any;
>_statementBrand : Symbol(Statement1._statementBrand, Decl(coAndContraVariantInferences2.ts, 72, 36))
}
interface ClassExpression1 extends Node1 {
>ClassExpression1 : Symbol(ClassExpression1, Decl(coAndContraVariantInferences2.ts, 74, 1))
>Node1 : Symbol(Node1, Decl(coAndContraVariantInferences2.ts, 66, 1))
kind: SyntaxKind1.ClassExpression;
>kind : Symbol(ClassExpression1.kind, Decl(coAndContraVariantInferences2.ts, 76, 42))
>SyntaxKind1 : Symbol(SyntaxKind1, Decl(coAndContraVariantInferences2.ts, 59, 1))
>ClassExpression : Symbol(SyntaxKind1.ClassExpression, Decl(coAndContraVariantInferences2.ts, 63, 24))
}
interface ClassStatement1 extends Statement1 {
>ClassStatement1 : Symbol(ClassStatement1, Decl(coAndContraVariantInferences2.ts, 78, 1))
>Statement1 : Symbol(Statement1, Decl(coAndContraVariantInferences2.ts, 70, 1))
kind: SyntaxKind1.ClassStatement;
>kind : Symbol(ClassStatement1.kind, Decl(coAndContraVariantInferences2.ts, 80, 46))
>SyntaxKind1 : Symbol(SyntaxKind1, Decl(coAndContraVariantInferences2.ts, 59, 1))
>ClassStatement : Symbol(SyntaxKind1.ClassStatement, Decl(coAndContraVariantInferences2.ts, 64, 20))
}
type ClassLike1 = ClassExpression1 | ClassStatement1;
>ClassLike1 : Symbol(ClassLike1, Decl(coAndContraVariantInferences2.ts, 82, 1))
>ClassExpression1 : Symbol(ClassExpression1, Decl(coAndContraVariantInferences2.ts, 74, 1))
>ClassStatement1 : Symbol(ClassStatement1, Decl(coAndContraVariantInferences2.ts, 78, 1))
declare function isClassLike(node: Node1): node is ClassLike1;
>isClassLike : Symbol(isClassLike, Decl(coAndContraVariantInferences2.ts, 84, 53))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 86, 29))
>Node1 : Symbol(Node1, Decl(coAndContraVariantInferences2.ts, 66, 1))
>node : Symbol(node, Decl(coAndContraVariantInferences2.ts, 86, 29))
>ClassLike1 : Symbol(ClassLike1, Decl(coAndContraVariantInferences2.ts, 82, 1))
declare const statement: Statement1 | undefined;
>statement : Symbol(statement, Decl(coAndContraVariantInferences2.ts, 88, 13))
>Statement1 : Symbol(Statement1, Decl(coAndContraVariantInferences2.ts, 70, 1))
const maybeClassStatement = tryCast(statement, isClassLike); // ClassLike1
>maybeClassStatement : Symbol(maybeClassStatement, Decl(coAndContraVariantInferences2.ts, 90, 5))
>tryCast : Symbol(tryCast, Decl(coAndContraVariantInferences2.ts, 53, 62))
>statement : Symbol(statement, Decl(coAndContraVariantInferences2.ts, 88, 13))
>isClassLike : Symbol(isClassLike, Decl(coAndContraVariantInferences2.ts, 84, 53))
// Repro from #49924
interface TypeNode extends Node {
>TypeNode : Symbol(TypeNode, Decl(coAndContraVariantInferences2.ts, 90, 60))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
typeInfo: string;
>typeInfo : Symbol(TypeNode.typeInfo, Decl(coAndContraVariantInferences2.ts, 94, 33))
}
interface NodeArray<T extends Node> extends Array<T> {
>NodeArray : Symbol(NodeArray, Decl(coAndContraVariantInferences2.ts, 96, 1))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 98, 20))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 98, 20))
someProp: string;
>someProp : Symbol(NodeArray.someProp, Decl(coAndContraVariantInferences2.ts, 98, 54))
}
declare function isNodeArray<T extends Node>(array: readonly T[]): array is NodeArray<T>;
>isNodeArray : Symbol(isNodeArray, Decl(coAndContraVariantInferences2.ts, 100, 1))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 102, 29))
>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(coAndContraVariantInferences2.ts, 32, 1))
>array : Symbol(array, Decl(coAndContraVariantInferences2.ts, 102, 45))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 102, 29))
>array : Symbol(array, Decl(coAndContraVariantInferences2.ts, 102, 45))
>NodeArray : Symbol(NodeArray, Decl(coAndContraVariantInferences2.ts, 96, 1))
>T : Symbol(T, Decl(coAndContraVariantInferences2.ts, 102, 29))
declare const types: readonly TypeNode[];
>types : Symbol(types, Decl(coAndContraVariantInferences2.ts, 104, 13))
>TypeNode : Symbol(TypeNode, Decl(coAndContraVariantInferences2.ts, 90, 60))
const x = tryCast(types, isNodeArray); // NodeAray<TypeNode>
>x : Symbol(x, Decl(coAndContraVariantInferences2.ts, 106, 5))
>tryCast : Symbol(tryCast, Decl(coAndContraVariantInferences2.ts, 53, 62))
>types : Symbol(types, Decl(coAndContraVariantInferences2.ts, 104, 13))
>isNodeArray : Symbol(isNodeArray, Decl(coAndContraVariantInferences2.ts, 100, 1))

View File

@@ -0,0 +1,263 @@
=== tests/cases/compiler/coAndContraVariantInferences2.ts ===
interface A { a: string }
>a : string
interface B extends A { b: string }
>b : string
interface C extends A { c: string }
>c : string
declare function cast<T, U extends T>(x: T, test: (x: T) => x is U): U;
>cast : <T, U extends T>(x: T, test: (x: T) => x is U) => U
>x : T
>test : (x: T) => x is U
>x : T
declare function isC(x: A): x is C;
>isC : (x: A) => x is C
>x : A
function f1(a: A, b: B) {
>f1 : (a: A, b: B) => void
>a : A
>b : B
const x1 = cast(a, isC); // cast<A, C>
>x1 : C
>cast(a, isC) : C
>cast : <T, U extends T>(x: T, test: (x: T) => x is U) => U
>a : A
>isC : (x: A) => x is C
const x2 = cast(b, isC); // cast<A, C>
>x2 : C
>cast(b, isC) : C
>cast : <T, U extends T>(x: T, test: (x: T) => x is U) => U
>b : B
>isC : (x: A) => x is C
}
declare function useA(a: A): void;
>useA : (a: A) => void
>a : A
declare function consume<T, U extends T>(t: T, u: U, f: (x: T) => void): void;
>consume : <T, U extends T>(t: T, u: U, f: (x: T) => void) => void
>t : T
>u : U
>f : (x: T) => void
>x : T
function f2(b: B, c: C) {
>f2 : (b: B, c: C) => void
>b : B
>c : C
consume(b, c, useA); // consume<A, C>
>consume(b, c, useA) : void
>consume : <T, U extends T>(t: T, u: U, f: (x: T) => void) => void
>b : B
>c : C
>useA : (a: A) => void
consume(c, b, useA); // consume<A, B>
>consume(c, b, useA) : void
>consume : <T, U extends T>(t: T, u: U, f: (x: T) => void) => void
>c : C
>b : B
>useA : (a: A) => void
consume(b, b, useA); // consume<B, B>
>consume(b, b, useA) : void
>consume : <T, U extends T>(t: T, u: U, f: (x: T) => void) => void
>b : B
>b : B
>useA : (a: A) => void
consume(c, c, useA); // consume<C, C>
>consume(c, c, useA) : void
>consume : <T, U extends T>(t: T, u: U, f: (x: T) => void) => void
>c : C
>c : C
>useA : (a: A) => void
}
// Repro from #52111
enum SyntaxKind {
>SyntaxKind : SyntaxKind
Block,
>Block : SyntaxKind.Block
Identifier,
>Identifier : SyntaxKind.Identifier
CaseClause,
>CaseClause : SyntaxKind.CaseClause
FunctionExpression,
>FunctionExpression : SyntaxKind.FunctionExpression
FunctionDeclaration,
>FunctionDeclaration : SyntaxKind.FunctionDeclaration
}
interface Node { kind: SyntaxKind; }
>kind : SyntaxKind
interface Expression extends Node { _expressionBrand: any; }
>_expressionBrand : any
interface Declaration extends Node { _declarationBrand: any; }
>_declarationBrand : any
interface Block extends Node { kind: SyntaxKind.Block; }
>kind : SyntaxKind.Block
>SyntaxKind : any
interface Identifier extends Expression, Declaration { kind: SyntaxKind.Identifier; }
>kind : SyntaxKind.Identifier
>SyntaxKind : any
interface CaseClause extends Node { kind: SyntaxKind.CaseClause; }
>kind : SyntaxKind.CaseClause
>SyntaxKind : any
interface FunctionDeclaration extends Declaration { kind: SyntaxKind.FunctionDeclaration; }
>kind : SyntaxKind.FunctionDeclaration
>SyntaxKind : any
type HasLocals = Block | FunctionDeclaration;
>HasLocals : Block | FunctionDeclaration
declare function canHaveLocals(node: Node): node is HasLocals;
>canHaveLocals : (node: Node) => node is HasLocals
>node : Node
declare function assertNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U;
>assertNode : { <T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U; (node: Node | undefined, test: ((node: Node) => boolean) | undefined): void; }
>node : T | undefined
>test : (node: T) => node is U
>node : T
declare function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined): void;
>assertNode : { <T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U; (node: Node | undefined, test: ((node: Node) => boolean) | undefined): void; }
>node : Node | undefined
>test : ((node: Node) => boolean) | undefined
>node : Node
function foo(node: FunctionDeclaration | CaseClause) {
>foo : (node: FunctionDeclaration | CaseClause) => void
>node : CaseClause | FunctionDeclaration
assertNode(node, canHaveLocals); // assertNode<Node, HasLocals>
>assertNode(node, canHaveLocals) : void
>assertNode : { <T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U; (node: Node | undefined, test: ((node: Node) => boolean) | undefined): void; }
>node : CaseClause | FunctionDeclaration
>canHaveLocals : (node: Node) => node is HasLocals
node; // FunctionDeclaration
>node : FunctionDeclaration
}
declare function isExpression(node: Node): node is Expression;
>isExpression : (node: Node) => node is Expression
>node : Node
declare function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut;
>tryCast : <TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut) => TOut
>value : TIn | undefined
>test : (value: TIn) => value is TOut
>value : TIn
function bar(node: Identifier | FunctionDeclaration) {
>bar : (node: Identifier | FunctionDeclaration) => void
>node : Identifier | FunctionDeclaration
const a = tryCast(node, isExpression); // tryCast<Expression, Node>
>a : Expression
>tryCast(node, isExpression) : Expression
>tryCast : <TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut) => TOut
>node : Identifier | FunctionDeclaration
>isExpression : (node: Node) => node is Expression
}
// Repro from #49924
const enum SyntaxKind1 {
>SyntaxKind1 : SyntaxKind1
ClassExpression,
>ClassExpression : SyntaxKind1.ClassExpression
ClassStatement,
>ClassStatement : SyntaxKind1.ClassStatement
}
interface Node1 {
kind: SyntaxKind1;
>kind : SyntaxKind1
}
interface Statement1 extends Node1 {
_statementBrand: any;
>_statementBrand : any
}
interface ClassExpression1 extends Node1 {
kind: SyntaxKind1.ClassExpression;
>kind : SyntaxKind1.ClassExpression
>SyntaxKind1 : any
}
interface ClassStatement1 extends Statement1 {
kind: SyntaxKind1.ClassStatement;
>kind : SyntaxKind1.ClassStatement
>SyntaxKind1 : any
}
type ClassLike1 = ClassExpression1 | ClassStatement1;
>ClassLike1 : ClassExpression1 | ClassStatement1
declare function isClassLike(node: Node1): node is ClassLike1;
>isClassLike : (node: Node1) => node is ClassLike1
>node : Node1
declare const statement: Statement1 | undefined;
>statement : Statement1 | undefined
const maybeClassStatement = tryCast(statement, isClassLike); // ClassLike1
>maybeClassStatement : ClassLike1
>tryCast(statement, isClassLike) : ClassLike1
>tryCast : <TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut) => TOut
>statement : Statement1 | undefined
>isClassLike : (node: Node1) => node is ClassLike1
// Repro from #49924
interface TypeNode extends Node {
typeInfo: string;
>typeInfo : string
}
interface NodeArray<T extends Node> extends Array<T> {
someProp: string;
>someProp : string
}
declare function isNodeArray<T extends Node>(array: readonly T[]): array is NodeArray<T>;
>isNodeArray : <T extends Node>(array: readonly T[]) => array is NodeArray<T>
>array : readonly T[]
declare const types: readonly TypeNode[];
>types : readonly TypeNode[]
const x = tryCast(types, isNodeArray); // NodeAray<TypeNode>
>x : NodeArray<TypeNode>
>tryCast(types, isNodeArray) : NodeArray<TypeNode>
>tryCast : <TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut) => TOut
>types : readonly TypeNode[]
>isNodeArray : <T extends Node>(array: readonly T[]) => array is NodeArray<T>

View File

@@ -0,0 +1,109 @@
// @strict: true
interface A { a: string }
interface B extends A { b: string }
interface C extends A { c: string }
declare function cast<T, U extends T>(x: T, test: (x: T) => x is U): U;
declare function isC(x: A): x is C;
function f1(a: A, b: B) {
const x1 = cast(a, isC); // cast<A, C>
const x2 = cast(b, isC); // cast<A, C>
}
declare function useA(a: A): void;
declare function consume<T, U extends T>(t: T, u: U, f: (x: T) => void): void;
function f2(b: B, c: C) {
consume(b, c, useA); // consume<A, C>
consume(c, b, useA); // consume<A, B>
consume(b, b, useA); // consume<B, B>
consume(c, c, useA); // consume<C, C>
}
// Repro from #52111
enum SyntaxKind {
Block,
Identifier,
CaseClause,
FunctionExpression,
FunctionDeclaration,
}
interface Node { kind: SyntaxKind; }
interface Expression extends Node { _expressionBrand: any; }
interface Declaration extends Node { _declarationBrand: any; }
interface Block extends Node { kind: SyntaxKind.Block; }
interface Identifier extends Expression, Declaration { kind: SyntaxKind.Identifier; }
interface CaseClause extends Node { kind: SyntaxKind.CaseClause; }
interface FunctionDeclaration extends Declaration { kind: SyntaxKind.FunctionDeclaration; }
type HasLocals = Block | FunctionDeclaration;
declare function canHaveLocals(node: Node): node is HasLocals;
declare function assertNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U): asserts node is U;
declare function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined): void;
function foo(node: FunctionDeclaration | CaseClause) {
assertNode(node, canHaveLocals); // assertNode<Node, HasLocals>
node; // FunctionDeclaration
}
declare function isExpression(node: Node): node is Expression;
declare function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut;
function bar(node: Identifier | FunctionDeclaration) {
const a = tryCast(node, isExpression); // tryCast<Expression, Node>
}
// Repro from #49924
const enum SyntaxKind1 {
ClassExpression,
ClassStatement,
}
interface Node1 {
kind: SyntaxKind1;
}
interface Statement1 extends Node1 {
_statementBrand: any;
}
interface ClassExpression1 extends Node1 {
kind: SyntaxKind1.ClassExpression;
}
interface ClassStatement1 extends Statement1 {
kind: SyntaxKind1.ClassStatement;
}
type ClassLike1 = ClassExpression1 | ClassStatement1;
declare function isClassLike(node: Node1): node is ClassLike1;
declare const statement: Statement1 | undefined;
const maybeClassStatement = tryCast(statement, isClassLike); // ClassLike1
// Repro from #49924
interface TypeNode extends Node {
typeInfo: string;
}
interface NodeArray<T extends Node> extends Array<T> {
someProp: string;
}
declare function isNodeArray<T extends Node>(array: readonly T[]): array is NodeArray<T>;
declare const types: readonly TypeNode[];
const x = tryCast(types, isNodeArray); // NodeAray<TypeNode>