-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issue
Milestone
Description
These are cases I've extracted from #49929; they are cases where if you don't specify the type parameters yourself, the inference isn't good.
This is potentially a full duplicate of #49924, however, the fix suggested in that issue does not work properly in our codebase. To test these, it may be easiest to just clone #49929 with the explicit type parameters removed and see what works and what doesn't.
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)
node;
// ^?
assertNode<Node, HasLocals>(node, canHaveLocals)
node;
// ^?
}
declare function isExpression(node: Node): node is Expression;
declare function cast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut;
function bar(node: Identifier | FunctionDeclaration) {
const a = cast(node, isExpression);
// ^?
const b = cast<Expression>(node, isExpression);
// ^?
}
Output
"use strict";
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);
node;
// ^?
assertNode(node, canHaveLocals);
node;
// ^?
}
function bar(node) {
const a = cast(node, isExpression);
// ^?
const b = cast(node, isExpression);
// ^?
}
Compiler Options
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2017",
"jsx": "react",
"module": "ESNext",
"moduleResolution": "node"
}
}
Playground Link: Provided
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issue