Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 13.7k
Introduce boolean literal freshness#27042
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -296,8 +296,8 @@ namespace ts { | ||
| createPromiseType, | ||
| createArrayType, | ||
| getBooleanType: () => booleanType, | ||
| getFalseType: () => falseType, | ||
| getTrueType: () => trueType, | ||
| getFalseType: (fresh?) => fresh ? falseType : regularFalseType, | ||
| getTrueType: (fresh?) => fresh ? trueType : regularTrueType, | ||
| getVoidType: () => voidType, | ||
| getUndefinedType: () => undefinedType, | ||
| getNullType: () => nullType, | ||
| @@ -405,9 +405,22 @@ namespace ts { | ||
| const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null"); | ||
| const stringType = createIntrinsicType(TypeFlags.String, "string"); | ||
| const numberType = createIntrinsicType(TypeFlags.Number, "number"); | ||
| const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false"); | ||
| const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true"); | ||
| const booleanType = createBooleanType([falseType, trueType]); | ||
| const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType; | ||
| const regularFalseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false") as FreshableIntrinsicType; | ||
| const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType; | ||
| const regularTrueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true") as FreshableIntrinsicType; | ||
| falseType.flags |= TypeFlags.FreshLiteral; | ||
| trueType.flags |= TypeFlags.FreshLiteral; | ||
| trueType.regularType = regularTrueType; | ||
| regularTrueType.freshType = trueType; | ||
| falseType.regularType = regularFalseType; | ||
| regularFalseType.freshType = falseType; | ||
| const booleanType = createBooleanType([regularFalseType, regularTrueType]); | ||
| // Also mark all combinations of fresh/regular booleans as "Boolean" so they print as `boolean` instead of `true | false` | ||
| // (The union is cached, so simply doing the marking here is sufficient) | ||
| createBooleanType([regularFalseType, trueType]); | ||
| createBooleanType([falseType, regularTrueType]); | ||
| createBooleanType([falseType, trueType]); | ||
| const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol"); | ||
| const voidType = createIntrinsicType(TypeFlags.Void, "void"); | ||
| const neverType = createIntrinsicType(TypeFlags.Never, "never"); | ||
| @@ -4170,7 +4183,7 @@ namespace ts { | ||
| const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : getBaseTypeOfEnumLiteralType(<LiteralType>t); | ||
| if (baseType.flags & TypeFlags.Union) { | ||
| const count = (<UnionType>baseType).types.length; | ||
| if (i + count <= types.length && types[i + count - 1] === (<UnionType>baseType).types[count - 1]) { | ||
| if (i + count <= types.length && getRegularTypeOfLiteralType(types[i + count - 1]) === getRegularTypeOfLiteralType((<UnionType>baseType).types[count - 1])) { | ||
| result.push(baseType); | ||
| i += count - 1; | ||
| continue; | ||
| @@ -6157,7 +6170,7 @@ namespace ts { | ||
| if (type.flags & TypeFlags.UniqueESSymbol) { | ||
| return `__@${type.symbol.escapedName}@${getSymbolId(type.symbol)}` as __String; | ||
| } | ||
| if (type.flags & TypeFlags.StringOrNumberLiteral) { | ||
| if (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) { | ||
| return escapeLeadingUnderscores("" + (<LiteralType>type).value); | ||
| } | ||
| return Debug.fail(); | ||
| @@ -8810,7 +8823,7 @@ namespace ts { | ||
| t.flags & TypeFlags.StringLiteral && includes & TypeFlags.String || | ||
| t.flags & TypeFlags.NumberLiteral && includes & TypeFlags.Number || | ||
| t.flags & TypeFlags.UniqueESSymbol && includes & TypeFlags.ESSymbol || | ||
| t.flags & TypeFlags.StringOrNumberLiteral && t.flags & TypeFlags.FreshLiteral && containsType(types, (<LiteralType>t).regularType); | ||
| t.flags & TypeFlags.Literal && t.flags & TypeFlags.FreshLiteral && containsType(types, (<LiteralType>t).regularType); | ||
| if (remove) { | ||
| orderedRemoveItemAt(types, i); | ||
| } | ||
| @@ -9810,8 +9823,8 @@ namespace ts { | ||
| } | ||
| function getFreshTypeOfLiteralType(type: Type): Type { | ||
| if (type.flags & TypeFlags.StringOrNumberLiteral && !(type.flags & TypeFlags.FreshLiteral)) { | ||
| if (!(<LiteralType>type).freshType) { | ||
| if (type.flags & TypeFlags.Literal && !(type.flags & TypeFlags.FreshLiteral)) { | ||
| if (!(<LiteralType>type).freshType) { // NOTE: Safe because all freshable intrinsics always have fresh types already | ||
| const freshType = createLiteralType(type.flags | TypeFlags.FreshLiteral, (<LiteralType>type).value, (<LiteralType>type).symbol); | ||
| freshType.regularType = <LiteralType>type; | ||
| (<LiteralType>type).freshType = freshType; | ||
| @@ -9822,7 +9835,7 @@ namespace ts { | ||
| } | ||
| function getRegularTypeOfLiteralType(type: Type): Type { | ||
| return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType : | ||
| return type.flags & TypeFlags.Literal && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType : | ||
| type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getRegularTypeOfLiteralType)) : | ||
| type; | ||
| } | ||
| @@ -11035,11 +11048,11 @@ namespace ts { | ||
| } | ||
| function isTypeRelatedTo(source: Type, target: Type, relation: Map<RelationComparisonResult>) { | ||
| if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { | ||
| source = (<LiteralType>source).regularType; | ||
| if (source.flags & TypeFlags.Literal && source.flags & TypeFlags.FreshLiteral) { | ||
| source = (<FreshableType>source).regularType; | ||
| } | ||
| if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { | ||
| target = (<LiteralType>target).regularType; | ||
| if (target.flags & TypeFlags.Literal && target.flags & TypeFlags.FreshLiteral) { | ||
| target = (<FreshableType>target).regularType; | ||
| } | ||
| if (source === target || | ||
| relation === comparableRelation && !(target.flags & TypeFlags.Never) && isSimpleTypeRelatedTo(target, source, relation) || | ||
| @@ -11194,11 +11207,11 @@ namespace ts { | ||
| * * Ternary.False if they are not related. | ||
| */ | ||
| function isRelatedTo(source: Type, target: Type, reportErrors = false, headMessage?: DiagnosticMessage): Ternary { | ||
| if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { | ||
| source = (<LiteralType>source).regularType; | ||
| if (source.flags & TypeFlags.Literal && source.flags & TypeFlags.FreshLiteral) { | ||
| source = (<FreshableType>source).regularType; | ||
| } | ||
| if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { | ||
| target = (<LiteralType>target).regularType; | ||
| if (target.flags & TypeFlags.Literal && target.flags & TypeFlags.FreshLiteral) { | ||
| target = (<FreshableType>target).regularType; | ||
| } | ||
| if (source.flags & TypeFlags.Substitution) { | ||
| source = relation === definitelyAssignableRelation ? (<SubstitutionType>source).typeVariable : (<SubstitutionType>source).substitute; | ||
| @@ -12766,7 +12779,7 @@ namespace ts { | ||
| return type.flags & TypeFlags.EnumLiteral && type.flags & TypeFlags.FreshLiteral ? getBaseTypeOfEnumLiteralType(<LiteralType>type) : | ||
| type.flags & TypeFlags.StringLiteral && type.flags & TypeFlags.FreshLiteral ? stringType : | ||
| type.flags & TypeFlags.NumberLiteral && type.flags & TypeFlags.FreshLiteral ? numberType : | ||
| type.flags & TypeFlags.BooleanLiteral ? booleanType : | ||
| type.flags & TypeFlags.BooleanLiteral && type.flags & TypeFlags.FreshLiteral ? booleanType : | ||
| type.flags & TypeFlags.Union ? getUnionType(sameMap((<UnionType>type).types, getWidenedLiteralType)) : | ||
| type; | ||
| } | ||
| @@ -12820,7 +12833,7 @@ namespace ts { | ||
| return type.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((<UnionType>type).types) : | ||
| type.flags & TypeFlags.StringLiteral ? (<LiteralType>type).value === "" ? TypeFlags.StringLiteral : 0 : | ||
| type.flags & TypeFlags.NumberLiteral ? (<LiteralType>type).value === 0 ? TypeFlags.NumberLiteral : 0 : | ||
| type.flags & TypeFlags.BooleanLiteral ? type === falseType ? TypeFlags.BooleanLiteral : 0 : | ||
| type.flags & TypeFlags.BooleanLiteral ? (type === falseType || type === regularFalseType) ? TypeFlags.BooleanLiteral : 0 : | ||
| type.flags & TypeFlags.PossiblyFalsy; | ||
| } | ||
| @@ -12837,7 +12850,8 @@ namespace ts { | ||
| function getDefinitelyFalsyPartOfType(type: Type): Type { | ||
| return type.flags & TypeFlags.String ? emptyStringType : | ||
| type.flags & TypeFlags.Number ? zeroType : | ||
| type.flags & TypeFlags.Boolean || type === falseType ? falseType : | ||
| type.flags & TypeFlags.Boolean || type === regularFalseType ? regularFalseType : | ||
| type === falseType ? falseType : | ||
| type.flags & (TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null) || | ||
| type.flags & TypeFlags.StringLiteral && (<LiteralType>type).value === "" || | ||
| type.flags & TypeFlags.NumberLiteral && (<LiteralType>type).value === 0 ? type : | ||
| @@ -14092,7 +14106,10 @@ namespace ts { | ||
| if (assignedType.flags & TypeFlags.Never) { | ||
| return assignedType; | ||
| } | ||
| const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t)); | ||
| let reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t)); | ||
| if (assignedType.flags & (TypeFlags.FreshLiteral | TypeFlags.Literal)) { | ||
| reducedType = mapType(reducedType, getFreshTypeOfLiteralType); // Ensure that if the assignment is a fresh type, that we narrow to fresh types | ||
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. why didn't we have to do this before, for string and number literal types?
| ||
| } | ||
| // Our crude heuristic produces an invalid result in some cases: see GH#26130. | ||
| // For now, when that happens, we give up and don't narrow at all. (This also | ||
| // means we'll never narrow for erroneous assignments where the assigned type | ||
| @@ -14145,8 +14162,8 @@ namespace ts { | ||
| } | ||
| if (flags & TypeFlags.BooleanLike) { | ||
| return strictNullChecks ? | ||
| type === falseType ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts : | ||
| type === falseType ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; | ||
| (type === falseType || type === regularFalseType) ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts : | ||
| (type === falseType || type === regularFalseType) ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; | ||
| } | ||
| if (flags & TypeFlags.Object) { | ||
| return isFunctionObjectType(<ObjectType>type) ? | ||
| @@ -28351,19 +28368,20 @@ namespace ts { | ||
| function isLiteralConstDeclaration(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): boolean { | ||
| if (isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConst(node)) { | ||
| const type = getTypeOfSymbol(getSymbolOfNode(node)); | ||
| return !!(type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral); | ||
| return !!(type.flags & TypeFlags.Literal && type.flags & TypeFlags.FreshLiteral); | ||
| } | ||
| return false; | ||
| } | ||
| function literalTypeToNode(type: LiteralType, enclosing: Node): Expression { | ||
| const enumResult = type.flags & TypeFlags.EnumLiteral && nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing); | ||
| return enumResult || createLiteral(type.value); | ||
| function literalTypeToNode(type: FreshableType, enclosing: Node): Expression { | ||
| const enumResult = type.flags & TypeFlags.EnumLiteral ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing) | ||
| : type === trueType ? createTrue() : type === falseType && createFalse(); | ||
| return enumResult || createLiteral((type as LiteralType).value); | ||
| } | ||
| function createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration) { | ||
| const type = getTypeOfSymbol(getSymbolOfNode(node)); | ||
| return literalTypeToNode(<LiteralType>type, node); | ||
| return literalTypeToNode(<FreshableType>type, node); | ||
| } | ||
| function createResolver(): EmitResolver { | ||
| @@ -29735,7 +29753,7 @@ namespace ts { | ||
| function checkAmbientInitializer(node: VariableDeclaration | PropertyDeclaration | PropertySignature) { | ||
| if (node.initializer) { | ||
| const isInvalidInitializer = !(isStringOrNumberLiteralExpression(node.initializer) || isSimpleLiteralEnumReference(node.initializer)); | ||
| const isInvalidInitializer = !(isStringOrNumberLiteralExpression(node.initializer) || isSimpleLiteralEnumReference(node.initializer) || node.initializer.kind === SyntaxKind.TrueKeyword || node.initializer.kind === SyntaxKind.FalseKeyword); | ||
| const isConstOrReadonly = isDeclarationReadonly(node) || isVariableDeclaration(node) && isVarConst(node); | ||
| if (isConstOrReadonly && !node.type) { | ||
| if (isInvalidInitializer) { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3034,8 +3034,8 @@ namespace ts { | ||
| /* @internal */ getStringType(): Type; | ||
| /* @internal */ getNumberType(): Type; | ||
| /* @internal */ getBooleanType(): Type; | ||
| /* @internal */ getFalseType(): Type; | ||
| /* @internal */ getTrueType(): Type; | ||
| /* @internal */ getFalseType(fresh?: boolean): Type; | ||
| /* @internal */ getTrueType(fresh?: boolean): Type; | ||
| /* @internal */ getVoidType(): Type; | ||
| /* @internal */ getUndefinedType(): Type; | ||
| /* @internal */ getNullType(): Type; | ||
| @@ -3731,7 +3731,7 @@ namespace ts { | ||
| Unit = Literal | UniqueESSymbol | Nullable, | ||
| StringOrNumberLiteral = StringLiteral | NumberLiteral, | ||
| /* @internal */ | ||
| StringOrNumberLiteralOrUnique = StringOrNumberLiteral | UniqueESSymbol, | ||
| StringOrNumberLiteralOrUnique = StringLiteral | NumberLiteral | UniqueESSymbol, | ||
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 change doesn't seem related (and I'm not sure it's an improvement?)
| ||
| /* @internal */ | ||
| DefinitelyFalsy = StringLiteral | NumberLiteral | BooleanLiteral | Void | Undefined | Null, | ||
| PossiblyFalsy = DefinitelyFalsy | String | Number | Boolean, | ||
| @@ -3802,6 +3802,15 @@ namespace ts { | ||
| intrinsicName: string; // Name of intrinsic type | ||
| } | ||
| /* @internal */ | ||
| export interface FreshableIntrinsicType extends IntrinsicType { | ||
| freshType: IntrinsicType; // Fresh version of type | ||
| regularType: IntrinsicType; // Regular version of type | ||
| } | ||
| /* @internal */ | ||
| export type FreshableType = LiteralType | FreshableIntrinsicType; | ||
| // String literal types (TypeFlags.StringLiteral) | ||
| // Numeric literal types (TypeFlags.NumberLiteral) | ||
| export interface LiteralType extends Type { | ||
Uh oh!
There was an error while loading. Please reload this page.
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.
can we ever get a union of
falseType | regularFalseType? Note that it doesn't matter how we print that, I'm just curious whether it can happen.Uh oh!
There was an error while loading. Please reload this page.
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.
Uhmmm, I don't think so, just like how we can't have
"word" | "word"(fresh edition), since both have the same type "identity". Specifically,removeRedundantLiteralTypeshandles removing redundant fresh types when the regular one is also present.