mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-17 10:31:20 -06:00
Initial implementation of conditional type operator
This commit is contained in:
parent
334bf4eaf7
commit
57ca7680c9
@ -3416,6 +3416,7 @@ namespace ts {
|
||||
case SyntaxKind.TupleType:
|
||||
case SyntaxKind.UnionType:
|
||||
case SyntaxKind.IntersectionType:
|
||||
case SyntaxKind.ConditionalType:
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
|
||||
@ -2620,6 +2620,13 @@ namespace ts {
|
||||
const indexTypeNode = typeToTypeNodeHelper((<IndexedAccessType>type).indexType, context);
|
||||
return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
const checkTypeNode = typeToTypeNodeHelper((<ConditionalType>type).checkType, context);
|
||||
const extendskTypeNode = typeToTypeNodeHelper((<ConditionalType>type).extendsType, context);
|
||||
const trueTypeNode = typeToTypeNodeHelper((<ConditionalType>type).trueType, context);
|
||||
const falseTypeNode = typeToTypeNodeHelper((<ConditionalType>type).falseType, context);
|
||||
return createConditionalTypeNode(checkTypeNode, extendskTypeNode, trueTypeNode, falseTypeNode);
|
||||
}
|
||||
|
||||
Debug.fail("Should be unreachable.");
|
||||
|
||||
@ -3388,6 +3395,15 @@ namespace ts {
|
||||
writeType((<IndexedAccessType>type).indexType, TypeFormatFlags.None);
|
||||
writePunctuation(writer, SyntaxKind.CloseBracketToken);
|
||||
}
|
||||
else if (type.flags & TypeFlags.Conditional) {
|
||||
writeType((<ConditionalType>type).checkType, TypeFormatFlags.InElementType);
|
||||
writer.writeKeyword("extends");
|
||||
writeType((<ConditionalType>type).extendsType, TypeFormatFlags.InElementType);
|
||||
writePunctuation(writer, SyntaxKind.QuestionToken);
|
||||
writeType((<ConditionalType>type).trueType, TypeFormatFlags.InElementType);
|
||||
writePunctuation(writer, SyntaxKind.ColonToken);
|
||||
writeType((<ConditionalType>type).falseType, TypeFormatFlags.InElementType);
|
||||
}
|
||||
else {
|
||||
// Should never get here
|
||||
// { ... }
|
||||
@ -8189,6 +8205,35 @@ namespace ts {
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getConditionalType(checkType: Type, extendsType: Type, trueType: Type, falseType: Type, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
|
||||
if (checkType.flags & TypeFlags.Union) {
|
||||
return getUnionType(map((<UnionType>checkType).types, t => getConditionalType(t, extendsType, trueType, falseType)),
|
||||
/*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
|
||||
}
|
||||
if (isTypeAssignableTo(checkType, extendsType)) {
|
||||
return trueType;
|
||||
}
|
||||
if (!isGenericObjectType(checkType) && !isGenericObjectType(extendsType)) {
|
||||
return falseType;
|
||||
}
|
||||
const type = <ConditionalType>createType(TypeFlags.Conditional);
|
||||
type.checkType = checkType;
|
||||
type.extendsType = extendsType;
|
||||
type.trueType = trueType;
|
||||
type.falseType = falseType;
|
||||
return type;
|
||||
}
|
||||
|
||||
function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = getConditionalType(getTypeFromTypeNode(node.checkType), getTypeFromTypeNode(node.extendsType),
|
||||
getTypeFromTypeNode(node.trueType), getTypeFromTypeNode(node.falseType),
|
||||
getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
|
||||
function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: TypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
@ -8481,6 +8526,8 @@ namespace ts {
|
||||
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
|
||||
case SyntaxKind.MappedType:
|
||||
return getTypeFromMappedTypeNode(<MappedTypeNode>node);
|
||||
case SyntaxKind.ConditionalType:
|
||||
return getTypeFromConditionalTypeNode(<ConditionalTypeNode>node);
|
||||
// This function assumes that an identifier or qualified name is a type expression
|
||||
// Callers should first ensure this by calling isTypeNode
|
||||
case SyntaxKind.Identifier:
|
||||
@ -8778,6 +8825,11 @@ namespace ts {
|
||||
if (type.flags & TypeFlags.IndexedAccess) {
|
||||
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
|
||||
}
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
return getConditionalType(instantiateType((<ConditionalType>type).checkType, mapper), instantiateType((<ConditionalType>type).extendsType, mapper),
|
||||
instantiateType((<ConditionalType>type).trueType, mapper), instantiateType((<ConditionalType>type).falseType, mapper),
|
||||
type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper));
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -450,6 +450,8 @@ namespace ts {
|
||||
return emitUnionType(<UnionTypeNode>type);
|
||||
case SyntaxKind.IntersectionType:
|
||||
return emitIntersectionType(<IntersectionTypeNode>type);
|
||||
case SyntaxKind.ConditionalType:
|
||||
return emitConditionalType(<ConditionalTypeNode>type);
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
return emitParenType(<ParenthesizedTypeNode>type);
|
||||
case SyntaxKind.TypeOperator:
|
||||
@ -545,7 +547,17 @@ namespace ts {
|
||||
emitSeparatedList(type.types, " & ", emitType);
|
||||
}
|
||||
|
||||
function emitParenType(type: ParenthesizedTypeNode) {
|
||||
function emitConditionalType(node: ConditionalTypeNode) {
|
||||
emitType(node.checkType);
|
||||
write(" extends ");
|
||||
emitType(node.extendsType);
|
||||
write(" ? ");
|
||||
emitType(node.trueType);
|
||||
write(" : ");
|
||||
emitType(node.falseType);
|
||||
}
|
||||
|
||||
function emitParenType(type: ParenthesizedTypeNode) {
|
||||
write("(");
|
||||
emitType(type.type);
|
||||
write(")");
|
||||
|
||||
@ -562,6 +562,8 @@ namespace ts {
|
||||
return emitUnionType(<UnionTypeNode>node);
|
||||
case SyntaxKind.IntersectionType:
|
||||
return emitIntersectionType(<IntersectionTypeNode>node);
|
||||
case SyntaxKind.ConditionalType:
|
||||
return emitConditionalType(<ConditionalTypeNode>node);
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
return emitParenthesizedType(<ParenthesizedTypeNode>node);
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
@ -1129,6 +1131,16 @@ namespace ts {
|
||||
emitList(node, node.types, ListFormat.IntersectionTypeConstituents);
|
||||
}
|
||||
|
||||
function emitConditionalType(node: ConditionalTypeNode) {
|
||||
emit(node.checkType);
|
||||
write(" extends ");
|
||||
emit(node.extendsType);
|
||||
write(" ? ");
|
||||
emit(node.trueType);
|
||||
write(" : ");
|
||||
emit(node.falseType);
|
||||
}
|
||||
|
||||
function emitParenthesizedType(node: ParenthesizedTypeNode) {
|
||||
write("(");
|
||||
emit(node.type);
|
||||
|
||||
@ -720,6 +720,24 @@ namespace ts {
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) {
|
||||
const node = createSynthesizedNode(SyntaxKind.ConditionalType) as ConditionalTypeNode;
|
||||
node.checkType = parenthesizeConditionalTypeMember(checkType);
|
||||
node.extendsType = parenthesizeConditionalTypeMember(extendsType);
|
||||
node.trueType = trueType;
|
||||
node.falseType = falseType;
|
||||
return node;
|
||||
}
|
||||
|
||||
export function updateConditionalTypeNode(node: ConditionalTypeNode, checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) {
|
||||
return node.checkType !== checkType
|
||||
|| node.extendsType !== extendsType
|
||||
|| node.trueType !== trueType
|
||||
|| node.falseType !== falseType
|
||||
? updateNode(createConditionalTypeNode(checkType, extendsType, trueType, falseType), node)
|
||||
: node;
|
||||
}
|
||||
|
||||
export function createParenthesizedType(type: TypeNode) {
|
||||
const node = <ParenthesizedTypeNode>createSynthesizedNode(SyntaxKind.ParenthesizedType);
|
||||
node.type = type;
|
||||
@ -4081,6 +4099,10 @@ namespace ts {
|
||||
return expression;
|
||||
}
|
||||
|
||||
export function parenthesizeConditionalTypeMember(member: TypeNode) {
|
||||
return member.kind === SyntaxKind.ConditionalType ? createParenthesizedType(member) : member;
|
||||
}
|
||||
|
||||
export function parenthesizeElementTypeMember(member: TypeNode) {
|
||||
switch (member.kind) {
|
||||
case SyntaxKind.UnionType:
|
||||
@ -4089,7 +4111,7 @@ namespace ts {
|
||||
case SyntaxKind.ConstructorType:
|
||||
return createParenthesizedType(member);
|
||||
}
|
||||
return member;
|
||||
return parenthesizeConditionalTypeMember(member);
|
||||
}
|
||||
|
||||
export function parenthesizeArrayTypeMember(member: TypeNode) {
|
||||
|
||||
@ -147,6 +147,11 @@ namespace ts {
|
||||
case SyntaxKind.UnionType:
|
||||
case SyntaxKind.IntersectionType:
|
||||
return visitNodes(cbNode, cbNodes, (<UnionOrIntersectionTypeNode>node).types);
|
||||
case SyntaxKind.ConditionalType:
|
||||
return visitNode(cbNode, (<ConditionalTypeNode>node).checkType) ||
|
||||
visitNode(cbNode, (<ConditionalTypeNode>node).extendsType) ||
|
||||
visitNode(cbNode, (<ConditionalTypeNode>node).trueType) ||
|
||||
visitNode(cbNode, (<ConditionalTypeNode>node).falseType);
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.TypeOperator:
|
||||
return visitNode(cbNode, (<ParenthesizedTypeNode | TypeOperatorNode>node).type);
|
||||
@ -2760,6 +2765,10 @@ namespace ts {
|
||||
type = createJSDocPostfixType(SyntaxKind.JSDocNonNullableType, type);
|
||||
break;
|
||||
case SyntaxKind.QuestionToken:
|
||||
// only parse postfix ? inside jsdoc, otherwise it is a conditional type
|
||||
if (!(contextFlags & NodeFlags.JSDoc)) {
|
||||
return type;
|
||||
}
|
||||
type = createJSDocPostfixType(SyntaxKind.JSDocNullableType, type);
|
||||
break;
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
@ -2839,6 +2848,21 @@ namespace ts {
|
||||
return parseUnionOrIntersectionType(SyntaxKind.UnionType, parseIntersectionTypeOrHigher, SyntaxKind.BarToken);
|
||||
}
|
||||
|
||||
function parseConditionalTypeOrHigher(): TypeNode {
|
||||
const type = parseUnionTypeOrHigher();
|
||||
if (parseOptional(SyntaxKind.ExtendsKeyword)) {
|
||||
const node = <ConditionalTypeNode>createNode(SyntaxKind.ConditionalType, type.pos);
|
||||
node.checkType = type;
|
||||
node.extendsType = parseUnionTypeOrHigher();
|
||||
parseExpected(SyntaxKind.QuestionToken);
|
||||
node.trueType = parseConditionalTypeOrHigher();
|
||||
parseExpected(SyntaxKind.ColonToken);
|
||||
node.falseType = parseConditionalTypeOrHigher();
|
||||
return finishNode(node);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function isStartOfFunctionType(): boolean {
|
||||
if (token() === SyntaxKind.LessThanToken) {
|
||||
return true;
|
||||
@ -2928,7 +2952,7 @@ namespace ts {
|
||||
if (token() === SyntaxKind.NewKeyword) {
|
||||
return parseFunctionOrConstructorType(SyntaxKind.ConstructorType);
|
||||
}
|
||||
return parseUnionTypeOrHigher();
|
||||
return parseConditionalTypeOrHigher();
|
||||
}
|
||||
|
||||
function parseTypeAnnotation(): TypeNode {
|
||||
|
||||
@ -385,6 +385,7 @@ namespace ts {
|
||||
case SyntaxKind.TypeReference:
|
||||
case SyntaxKind.UnionType:
|
||||
case SyntaxKind.IntersectionType:
|
||||
case SyntaxKind.ConditionalType:
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.ThisType:
|
||||
case SyntaxKind.TypeOperator:
|
||||
|
||||
@ -248,6 +248,7 @@ namespace ts {
|
||||
TupleType,
|
||||
UnionType,
|
||||
IntersectionType,
|
||||
ConditionalType,
|
||||
ParenthesizedType,
|
||||
ThisType,
|
||||
TypeOperator,
|
||||
@ -1065,6 +1066,14 @@ namespace ts {
|
||||
types: NodeArray<TypeNode>;
|
||||
}
|
||||
|
||||
export interface ConditionalTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.ConditionalType;
|
||||
checkType: TypeNode;
|
||||
extendsType: TypeNode;
|
||||
trueType: TypeNode;
|
||||
falseType: TypeNode;
|
||||
}
|
||||
|
||||
export interface ParenthesizedTypeNode extends TypeNode {
|
||||
kind: SyntaxKind.ParenthesizedType;
|
||||
type: TypeNode;
|
||||
@ -3339,18 +3348,19 @@ namespace ts {
|
||||
Intersection = 1 << 18, // Intersection (T & U)
|
||||
Index = 1 << 19, // keyof T
|
||||
IndexedAccess = 1 << 20, // T[K]
|
||||
Conditional = 1 << 21, // A extends B ? T : U
|
||||
/* @internal */
|
||||
FreshLiteral = 1 << 21, // Fresh literal or unique type
|
||||
FreshLiteral = 1 << 22, // Fresh literal or unique type
|
||||
/* @internal */
|
||||
ContainsWideningType = 1 << 22, // Type is or contains undefined or null widening type
|
||||
ContainsWideningType = 1 << 23, // Type is or contains undefined or null widening type
|
||||
/* @internal */
|
||||
ContainsObjectLiteral = 1 << 23, // Type is or contains object literal type
|
||||
ContainsObjectLiteral = 1 << 24, // Type is or contains object literal type
|
||||
/* @internal */
|
||||
ContainsAnyFunctionType = 1 << 24, // Type is or contains the anyFunctionType
|
||||
NonPrimitive = 1 << 25, // intrinsic object type
|
||||
ContainsAnyFunctionType = 1 << 25, // Type is or contains the anyFunctionType
|
||||
NonPrimitive = 1 << 26, // intrinsic object type
|
||||
/* @internal */
|
||||
JsxAttributes = 1 << 26, // Jsx attributes type
|
||||
MarkerType = 1 << 27, // Marker type used for variance probing
|
||||
JsxAttributes = 1 << 27, // Jsx attributes type
|
||||
MarkerType = 1 << 28, // Marker type used for variance probing
|
||||
|
||||
/* @internal */
|
||||
Nullable = Undefined | Null,
|
||||
@ -3373,12 +3383,12 @@ namespace ts {
|
||||
ESSymbolLike = ESSymbol | UniqueESSymbol,
|
||||
UnionOrIntersection = Union | Intersection,
|
||||
StructuredType = Object | Union | Intersection,
|
||||
StructuredOrTypeVariable = StructuredType | TypeParameter | Index | IndexedAccess,
|
||||
TypeVariable = TypeParameter | IndexedAccess,
|
||||
TypeVariable = TypeParameter | IndexedAccess | Conditional,
|
||||
StructuredOrTypeVariable = StructuredType | TypeVariable | Index,
|
||||
|
||||
// 'Narrowable' types are types where narrowing actually narrows.
|
||||
// This *should* be every type other than null, undefined, void, and never
|
||||
Narrowable = Any | StructuredType | TypeParameter | Index | IndexedAccess | StringLike | NumberLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
|
||||
Narrowable = Any | StructuredOrTypeVariable | StringLike | NumberLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
|
||||
NotUnionOrUnit = Any | ESSymbol | Object | NonPrimitive,
|
||||
/* @internal */
|
||||
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
|
||||
@ -3626,6 +3636,13 @@ namespace ts {
|
||||
type: TypeVariable | UnionOrIntersectionType;
|
||||
}
|
||||
|
||||
export interface ConditionalType extends TypeVariable {
|
||||
checkType: Type;
|
||||
extendsType: Type;
|
||||
trueType: Type;
|
||||
falseType: Type;
|
||||
}
|
||||
|
||||
export const enum SignatureKind {
|
||||
Call,
|
||||
Construct,
|
||||
|
||||
@ -4527,6 +4527,10 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.IntersectionType;
|
||||
}
|
||||
|
||||
export function isConditionalTypeNode(node: Node): node is ConditionalTypeNode {
|
||||
return node.kind === SyntaxKind.ConditionalType;
|
||||
}
|
||||
|
||||
export function isParenthesizedTypeNode(node: Node): node is ParenthesizedTypeNode {
|
||||
return node.kind === SyntaxKind.ParenthesizedType;
|
||||
}
|
||||
|
||||
@ -385,6 +385,13 @@ namespace ts {
|
||||
return updateIntersectionTypeNode(<IntersectionTypeNode>node,
|
||||
nodesVisitor((<IntersectionTypeNode>node).types, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.ConditionalType:
|
||||
return updateConditionalTypeNode(<ConditionalTypeNode>node,
|
||||
visitNode((<ConditionalTypeNode>node).checkType, visitor, isTypeNode),
|
||||
visitNode((<ConditionalTypeNode>node).extendsType, visitor, isTypeNode),
|
||||
visitNode((<ConditionalTypeNode>node).trueType, visitor, isTypeNode),
|
||||
visitNode((<ConditionalTypeNode>node).falseType, visitor, isTypeNode));
|
||||
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
return updateParenthesizedType(<ParenthesizedTypeNode>node,
|
||||
visitNode((<ParenthesizedTypeNode>node).type, visitor, isTypeNode));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user