-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Discriminated union types #9163
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
Changes from 1 commit
4a8f94a
ce15646
c90b0fe
00376f4
5ff7c29
a3635cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5160,7 +5160,6 @@ namespace ts { | |
| if (hasProperty(stringLiteralTypes, text)) { | ||
| return stringLiteralTypes[text]; | ||
| } | ||
|
|
||
| const type = stringLiteralTypes[text] = <StringLiteralType>createType(TypeFlags.StringLiteral); | ||
| type.text = text; | ||
| return type; | ||
|
|
@@ -5625,6 +5624,10 @@ namespace ts { | |
| return checkTypeComparableTo(source, target, /*errorNode*/ undefined); | ||
| } | ||
|
|
||
| function areTypesComparable(type1: Type, type2: Type): boolean { | ||
| return isTypeComparableTo(type1, type2) || isTypeComparableTo(type2, type1); | ||
| } | ||
|
|
||
| function checkTypeSubtypeOf(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean { | ||
| return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, headMessage, containingMessageChain); | ||
| } | ||
|
|
@@ -6805,8 +6808,10 @@ namespace ts { | |
| return !!getPropertyOfType(type, "0"); | ||
| } | ||
|
|
||
| function isStringLiteralType(type: Type) { | ||
| return type.flags & TypeFlags.StringLiteral; | ||
| function isStringLiteralUnionType(type: Type): boolean { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should really be
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can be either a single string literal type or a union of string literal types. I'm not sure your suggestion better covers that. |
||
| return type.flags & TypeFlags.StringLiteral ? true : | ||
| type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isStringLiteralUnionType) : | ||
| false; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -7873,6 +7878,9 @@ namespace ts { | |
| if (isNullOrUndefinedLiteral(expr.right)) { | ||
| return narrowTypeByNullCheck(type, expr, assumeTrue); | ||
| } | ||
| if (expr.left.kind === SyntaxKind.PropertyAccessExpression) { | ||
| return narrowTypeByDiscriminant(type, expr, assumeTrue); | ||
| } | ||
| if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral) { | ||
| return narrowTypeByTypeof(type, expr, assumeTrue); | ||
| } | ||
|
|
@@ -7903,6 +7911,33 @@ namespace ts { | |
| return getTypeWithFacts(type, facts); | ||
| } | ||
|
|
||
| function narrowTypeByDiscriminant(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { | ||
| // We have '==', '!=', '===', or '!==' operator with property access on left | ||
| if (!(type.flags & TypeFlags.Union) || !isMatchingReference(reference, (<PropertyAccessExpression>expr.left).expression)) { | ||
| return type; | ||
| } | ||
| const propName = (<PropertyAccessExpression>expr.left).name.text; | ||
| const propType = getTypeOfPropertyOfType(type, propName); | ||
| if (!propType || !isStringLiteralUnionType(propType)) { | ||
| return type; | ||
| } | ||
| const discriminantType = expr.right.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((<StringLiteral>expr.right).text) : checkExpression(expr.right); | ||
| if (!isStringLiteralUnionType(discriminantType)) { | ||
| return type; | ||
| } | ||
| if (expr.operatorToken.kind === SyntaxKind.ExclamationEqualsToken || | ||
| expr.operatorToken.kind === SyntaxKind.ExclamationEqualsEqualsToken) { | ||
| assumeTrue = !assumeTrue; | ||
| } | ||
| if (assumeTrue) { | ||
| return getUnionType(filter((<UnionType>type).types, t => areTypesComparable(getTypeOfPropertyOfType(t, propName), discriminantType))); | ||
| } | ||
| if (discriminantType.flags & TypeFlags.StringLiteral) { | ||
| return getUnionType(filter((<UnionType>type).types, t => getTypeOfPropertyOfType(t, propName) !== discriminantType)); | ||
| } | ||
| return type; | ||
| } | ||
|
|
||
| function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { | ||
| // We have '==', '!=', '====', or !==' operator with 'typeof xxx' on the left | ||
| // and string literal on the right | ||
|
|
@@ -8892,10 +8927,6 @@ namespace ts { | |
| return applyToContextualType(type, t => getIndexTypeOfStructuredType(t, kind)); | ||
| } | ||
|
|
||
| function contextualTypeIsStringLiteralType(type: Type): boolean { | ||
| return !!(type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isStringLiteralType) : isStringLiteralType(type)); | ||
| } | ||
|
|
||
| // Return true if the given contextual type is a tuple-like type | ||
| function contextualTypeIsTupleLikeType(type: Type): boolean { | ||
| return !!(type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isTupleLikeType) : isTupleLikeType(type)); | ||
|
|
@@ -12557,7 +12588,7 @@ namespace ts { | |
|
|
||
| function checkStringLiteralExpression(node: StringLiteral): Type { | ||
| const contextualType = getContextualType(node); | ||
| if (contextualType && contextualTypeIsStringLiteralType(contextualType)) { | ||
| if (contextualType && isStringLiteralUnionType(contextualType)) { | ||
| return getStringLiteralTypeForText(node.text); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
Add a comment that this relationship is bidirectional and doesn't report errors.