-
Notifications
You must be signed in to change notification settings - Fork 12.8k
More prolific literal types #19587
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
More prolific literal types #19587
Changes from all commits
12d1f72
541e42e
e8cd3b5
577383e
dfaf67b
489321b
fec134d
254d464
542deae
5001204
b48d378
1a71eea
fccfd13
1268fab
840c600
9c1bd45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4268,9 +4268,10 @@ namespace ts { | |
if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { | ||
type = getTypeWithFacts(type, TypeFacts.NEUndefined); | ||
} | ||
return declaration.initializer ? | ||
type = declaration.initializer ? | ||
getUnionType([type, checkExpressionCached(declaration.initializer)], /*subtypeReduction*/ true) : | ||
type; | ||
return shouldUseLiteralType(declaration) ? type : getWidenedLiteralType(type); | ||
} | ||
|
||
function getTypeForDeclarationFromJSDocComment(declaration: Node) { | ||
|
@@ -9030,6 +9031,11 @@ namespace ts { | |
* * Ternary.False if they are not related. | ||
*/ | ||
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary { | ||
return (relation === comparableRelation && isRelatedToMonodirectional(target, source, /*reportErrors*/ false, headMessage)) | ||
|| isRelatedToMonodirectional(source, target, reportErrors, headMessage); | ||
} | ||
|
||
function isRelatedToMonodirectional(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary { | ||
if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { | ||
source = (<LiteralType>source).regularType; | ||
} | ||
|
@@ -9236,14 +9242,14 @@ namespace ts { | |
return Ternary.True; | ||
} | ||
for (const type of targetTypes) { | ||
const related = isRelatedTo(source, type, /*reportErrors*/ false); | ||
const related = isRelatedToMonodirectional(source, type, /*reportErrors*/ false); | ||
if (related) { | ||
return related; | ||
} | ||
} | ||
if (reportErrors) { | ||
const discriminantType = findMatchingDiscriminantType(source, target); | ||
isRelatedTo(source, discriminantType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true); | ||
isRelatedToMonodirectional(source, discriminantType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true); | ||
} | ||
return Ternary.False; | ||
} | ||
|
@@ -9273,7 +9279,7 @@ namespace ts { | |
let result = Ternary.True; | ||
const targetTypes = target.types; | ||
for (const targetType of targetTypes) { | ||
const related = isRelatedTo(source, targetType, reportErrors); | ||
const related = isRelatedToMonodirectional(source, targetType, reportErrors); | ||
if (!related) { | ||
return Ternary.False; | ||
} | ||
|
@@ -9289,7 +9295,7 @@ namespace ts { | |
} | ||
const len = sourceTypes.length; | ||
for (let i = 0; i < len; i++) { | ||
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1); | ||
const related = isRelatedToMonodirectional(sourceTypes[i], target, reportErrors && i === len - 1); | ||
if (related) { | ||
return related; | ||
} | ||
|
@@ -9301,7 +9307,7 @@ namespace ts { | |
let result = Ternary.True; | ||
const sourceTypes = source.types; | ||
for (const sourceType of sourceTypes) { | ||
const related = isRelatedTo(sourceType, target, reportErrors); | ||
const related = isRelatedToMonodirectional(sourceType, target, reportErrors); | ||
if (!related) { | ||
return Ternary.False; | ||
} | ||
|
@@ -9491,7 +9497,7 @@ namespace ts { | |
} | ||
// Report constraint errors only if the constraint is not the empty object type | ||
const reportConstraintErrors = reportErrors && constraint !== emptyObjectType; | ||
if (result = isRelatedTo(constraint, target, reportConstraintErrors)) { | ||
if (result = isRelatedToMonodirectional(constraint, target, reportConstraintErrors)) { | ||
errorInfo = saveErrorInfo; | ||
return result; | ||
} | ||
|
@@ -11758,7 +11764,7 @@ namespace ts { | |
} | ||
} | ||
} | ||
return mappedTypes ? getUnionType(mappedTypes) : mappedType; | ||
return mappedTypes ? getUnionType(mappedTypes, /*subtypeReduction*/ undefined, /*aliasSymbol*/ undefined, /*aliasTypeArguments*/ undefined) : mappedType; | ||
} | ||
|
||
function extractTypesOfKind(type: Type, kind: TypeFlags) { | ||
|
@@ -17350,7 +17356,7 @@ namespace ts { | |
if (isUnitType(type) && | ||
!(contextualSignature && | ||
isLiteralContextualType( | ||
contextualSignature === getSignatureFromDeclaration(func) ? type : getReturnTypeOfSignature(contextualSignature)))) { | ||
contextualSignature === getSignatureFromDeclaration(func) ? type : getReturnTypeOfSignature(contextualSignature), type))) { | ||
type = getWidenedLiteralType(type); | ||
} | ||
|
||
|
@@ -17849,7 +17855,7 @@ namespace ts { | |
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, | ||
// and the right operand to be of type Any, an object type, or a type parameter type. | ||
// The result is always of the Boolean primitive type. | ||
if (!(isTypeComparableTo(leftType, stringType) || isTypeAssignableToKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) { | ||
if (!isTypeAssignableToKind(leftType, TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol)) { | ||
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); | ||
} | ||
if (!isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.TypeVariable)) { | ||
|
@@ -18434,24 +18440,28 @@ namespace ts { | |
|
||
function checkDeclarationInitializer(declaration: VariableLikeDeclaration) { | ||
const type = getTypeOfExpression(declaration.initializer, /*cache*/ true); | ||
return shouldUseLiteralType(declaration) ? type : getWidenedLiteralType(type); | ||
} | ||
|
||
function shouldUseLiteralType(declaration: VariableLikeDeclaration) { | ||
return getCombinedNodeFlags(declaration) & NodeFlags.Const || | ||
getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration) || | ||
isTypeAssertion(declaration.initializer) ? type : getWidenedLiteralType(type); | ||
(declaration.initializer && isTypeAssertion(declaration.initializer)); | ||
} | ||
|
||
function isLiteralContextualType(contextualType: Type) { | ||
function isLiteralContextualType(contextualType: Type, candidateLiteral: Type): boolean { | ||
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. Consider flipping the parameters and changing the name to canBeContextuallyTypedAsLiteral (or something similar; probably the parameter names would need to change too.) 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. @rbuckton 's dynamic names PR already includes both of those things, actually. |
||
if (contextualType) { | ||
if (contextualType.flags & TypeFlags.UnionOrIntersection) { | ||
return some((contextualType as UnionOrIntersectionType).types, t => isLiteralContextualType(t, candidateLiteral)); | ||
} | ||
if (contextualType.flags & TypeFlags.TypeVariable) { | ||
const constraint = getBaseConstraintOfType(contextualType) || emptyObjectType; | ||
// If the type parameter is constrained to the base primitive type we're checking for, | ||
// consider this a literal context. For example, given a type parameter 'T extends string', | ||
// this causes us to infer string literal types for T. | ||
if (constraint.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Boolean | TypeFlags.Enum)) { | ||
return true; | ||
} | ||
contextualType = constraint; | ||
return isLiteralContextualType(constraint, candidateLiteral); | ||
} | ||
return maybeTypeOfKind(contextualType, (TypeFlags.Literal | TypeFlags.Index)); | ||
// No need to `maybeTypeOfKind` on the contextual type, as it can't be a union, _however_, `candidateLiteral` might still be one! | ||
return !!(((contextualType.flags & TypeFlags.StringLike) && maybeTypeOfKind(candidateLiteral, TypeFlags.StringLike)) || | ||
((contextualType.flags & TypeFlags.NumberLike) && maybeTypeOfKind(candidateLiteral, TypeFlags.NumberLike)) || | ||
((contextualType.flags & TypeFlags.BooleanLike) && maybeTypeOfKind(candidateLiteral, TypeFlags.BooleanLike))); | ||
} | ||
return false; | ||
} | ||
|
@@ -18461,8 +18471,8 @@ namespace ts { | |
contextualType = getContextualType(node); | ||
} | ||
const type = checkExpression(node, checkMode); | ||
const shouldWiden = isTypeAssertion(node) || isLiteralContextualType(contextualType); | ||
return shouldWiden ? type : getWidenedLiteralType(type); | ||
const shouldNotWiden = isTypeAssertion(node) || isLiteralContextualType(contextualType, type); | ||
return shouldNotWiden ? type : getWidenedLiteralType(type); | ||
} | ||
|
||
function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type { | ||
|
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.
why only here and in the union/intersection type handling? why does it become bidirectional everywhere else?