From f2c028831d6d31010cc1eb100425ed01db27f901 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 11 Sep 2018 17:43:32 -0700 Subject: [PATCH] Introduce boolean literal freshness --- src/compiler/checker.ts | 80 ++++++++++++------- src/compiler/types.ts | 15 +++- .../codefixes/fixStrictClassInitialization.ts | 2 +- .../reference/ambientConstLiterals.js | 2 +- .../reference/booleanLiteralTypes1.types | 6 +- .../reference/booleanLiteralTypes2.types | 8 +- .../baselines/reference/constDeclarations.js | 2 +- .../baselines/reference/constDeclarations2.js | 2 +- .../logicalOrOperatorWithEveryType.types | 16 ++-- .../reference/numericLiteralTypes1.types | 6 +- .../reference/numericLiteralTypes2.types | 4 +- .../spreadBooleanRespectsFreshness.js | 11 +++ .../spreadBooleanRespectsFreshness.symbols | 30 +++++++ .../spreadBooleanRespectsFreshness.types | 32 ++++++++ .../spreadBooleanRespectsFreshness.ts | 7 ++ 15 files changed, 165 insertions(+), 58 deletions(-) create mode 100644 tests/baselines/reference/spreadBooleanRespectsFreshness.js create mode 100644 tests/baselines/reference/spreadBooleanRespectsFreshness.symbols create mode 100644 tests/baselines/reference/spreadBooleanRespectsFreshness.types create mode 100644 tests/cases/compiler/spreadBooleanRespectsFreshness.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a856df2854a12..8059daac7e278 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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(t); if (baseType.flags & TypeFlags.Union) { const count = (baseType).types.length; - if (i + count <= types.length && types[i + count - 1] === (baseType).types[count - 1]) { + if (i + count <= types.length && getRegularTypeOfLiteralType(types[i + count - 1]) === getRegularTypeOfLiteralType((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("" + (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, (t).regularType); + t.flags & TypeFlags.Literal && t.flags & TypeFlags.FreshLiteral && containsType(types, (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 (!(type).freshType) { + if (type.flags & TypeFlags.Literal && !(type.flags & TypeFlags.FreshLiteral)) { + if (!(type).freshType) { // NOTE: Safe because all freshable intrinsics always have fresh types already const freshType = createLiteralType(type.flags | TypeFlags.FreshLiteral, (type).value, (type).symbol); freshType.regularType = type; (type).freshType = freshType; @@ -9822,7 +9835,7 @@ namespace ts { } function getRegularTypeOfLiteralType(type: Type): Type { - return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (type).regularType : + return type.flags & TypeFlags.Literal && type.flags & TypeFlags.FreshLiteral ? (type).regularType : type.flags & TypeFlags.Union ? getUnionType(sameMap((type).types, getRegularTypeOfLiteralType)) : type; } @@ -11035,11 +11048,11 @@ namespace ts { } function isTypeRelatedTo(source: Type, target: Type, relation: Map) { - if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { - source = (source).regularType; + if (source.flags & TypeFlags.Literal && source.flags & TypeFlags.FreshLiteral) { + source = (source).regularType; } - if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { - target = (target).regularType; + if (target.flags & TypeFlags.Literal && target.flags & TypeFlags.FreshLiteral) { + target = (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 = (source).regularType; + if (source.flags & TypeFlags.Literal && source.flags & TypeFlags.FreshLiteral) { + source = (source).regularType; } - if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) { - target = (target).regularType; + if (target.flags & TypeFlags.Literal && target.flags & TypeFlags.FreshLiteral) { + target = (target).regularType; } if (source.flags & TypeFlags.Substitution) { source = relation === definitelyAssignableRelation ? (source).typeVariable : (source).substitute; @@ -12766,7 +12779,7 @@ namespace ts { return type.flags & TypeFlags.EnumLiteral && type.flags & TypeFlags.FreshLiteral ? getBaseTypeOfEnumLiteralType(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((type).types, getWidenedLiteralType)) : type; } @@ -12820,7 +12833,7 @@ namespace ts { return type.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((type).types) : type.flags & TypeFlags.StringLiteral ? (type).value === "" ? TypeFlags.StringLiteral : 0 : type.flags & TypeFlags.NumberLiteral ? (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 && (type).value === "" || type.flags & TypeFlags.NumberLiteral && (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 + } // 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(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(type, node); + return literalTypeToNode(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) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 279fef73d5e9f..9b509a9f2ab92 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -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, /* @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 { diff --git a/src/services/codefixes/fixStrictClassInitialization.ts b/src/services/codefixes/fixStrictClassInitialization.ts index 0647a64c36815..15c57c604ddf1 100644 --- a/src/services/codefixes/fixStrictClassInitialization.ts +++ b/src/services/codefixes/fixStrictClassInitialization.ts @@ -110,7 +110,7 @@ namespace ts.codefix { function getDefaultValueFromType (checker: TypeChecker, type: Type): Expression | undefined { if (type.flags & TypeFlags.BooleanLiteral) { - return type === checker.getFalseType() ? createFalse() : createTrue(); + return (type === checker.getFalseType() || type === checker.getFalseType(/*fresh*/ true)) ? createFalse() : createTrue(); } else if (type.isLiteral()) { return createLiteral(type.value); diff --git a/tests/baselines/reference/ambientConstLiterals.js b/tests/baselines/reference/ambientConstLiterals.js index b0b222128ace0..c5c0fbaafbc01 100644 --- a/tests/baselines/reference/ambientConstLiterals.js +++ b/tests/baselines/reference/ambientConstLiterals.js @@ -63,7 +63,7 @@ declare const c3 = "abc"; declare const c4 = 123; declare const c5 = 123; declare const c6 = -123; -declare const c7: boolean; +declare const c7 = true; declare const c8 = E.A; declare const c8b = E["non identifier"]; declare const c9: { diff --git a/tests/baselines/reference/booleanLiteralTypes1.types b/tests/baselines/reference/booleanLiteralTypes1.types index 8cd0a4331a7b5..c50ee4ab0ade1 100644 --- a/tests/baselines/reference/booleanLiteralTypes1.types +++ b/tests/baselines/reference/booleanLiteralTypes1.types @@ -82,13 +82,13 @@ function f4(t: true, f: false) { >false : false var x1 = t && f; ->x1 : boolean +>x1 : false >t && f : false >t : true >f : false var x2 = f && t; ->x2 : boolean +>x2 : false >f && t : false >f : false >t : true @@ -100,7 +100,7 @@ function f4(t: true, f: false) { >f : false var x4 = f || t; ->x4 : boolean +>x4 : true >f || t : true >f : false >t : true diff --git a/tests/baselines/reference/booleanLiteralTypes2.types b/tests/baselines/reference/booleanLiteralTypes2.types index 52e4d85e0bde3..15c8f1e9c5325 100644 --- a/tests/baselines/reference/booleanLiteralTypes2.types +++ b/tests/baselines/reference/booleanLiteralTypes2.types @@ -82,25 +82,25 @@ function f4(t: true, f: false) { >false : false var x1 = t && f; ->x1 : boolean +>x1 : false >t && f : false >t : true >f : false var x2 = f && t; ->x2 : boolean +>x2 : false >f && t : false >f : false >t : true var x3 = t || f; ->x3 : boolean +>x3 : true >t || f : true >t : true >f : false var x4 = f || t; ->x4 : boolean +>x4 : true >f || t : true >f : false >t : true diff --git a/tests/baselines/reference/constDeclarations.js b/tests/baselines/reference/constDeclarations.js index 273651179429e..19805831f72ce 100644 --- a/tests/baselines/reference/constDeclarations.js +++ b/tests/baselines/reference/constDeclarations.js @@ -24,6 +24,6 @@ for (const c5 = 0, c6 = 0; c5 < c6;) { //// [constDeclarations.d.ts] -declare const c1: boolean; +declare const c1 = false; declare const c2: number; declare const c3 = 0, c4: string, c5: any; diff --git a/tests/baselines/reference/constDeclarations2.js b/tests/baselines/reference/constDeclarations2.js index e19022154c0a7..f176e093610e7 100644 --- a/tests/baselines/reference/constDeclarations2.js +++ b/tests/baselines/reference/constDeclarations2.js @@ -19,7 +19,7 @@ var M; //// [constDeclarations2.d.ts] declare module M { - const c1: boolean; + const c1 = false; const c2: number; const c3 = 0, c4: string, c5: any; } diff --git a/tests/baselines/reference/logicalOrOperatorWithEveryType.types b/tests/baselines/reference/logicalOrOperatorWithEveryType.types index d84dc46ef732a..e4b36c988bb56 100644 --- a/tests/baselines/reference/logicalOrOperatorWithEveryType.types +++ b/tests/baselines/reference/logicalOrOperatorWithEveryType.types @@ -162,7 +162,7 @@ var rc1 = a1 || a3; // any || number is any >a3 : number var rc2 = a2 || a3; // boolean || number is boolean | number ->rc2 : number | boolean +>rc2 : number | true >a2 || a3 : number | true >a2 : boolean >a3 : number @@ -222,7 +222,7 @@ var rd1 = a1 || a4; // any || string is any >a4 : string var rd2 = a2 || a4; // boolean || string is boolean | string ->rd2 : string | boolean +>rd2 : string | true >a2 || a4 : string | true >a2 : boolean >a4 : string @@ -282,7 +282,7 @@ var re1 = a1 || a5; // any || void is any >a5 : void var re2 = a2 || a5; // boolean || void is boolean | void ->re2 : boolean | void +>re2 : true | void >a2 || a5 : true | void >a2 : boolean >a5 : void @@ -342,7 +342,7 @@ var rg1 = a1 || a6; // any || enum is any >a6 : E var rg2 = a2 || a6; // boolean || enum is boolean | enum ->rg2 : boolean | E +>rg2 : true | E >a2 || a6 : true | E >a2 : boolean >a6 : E @@ -402,7 +402,7 @@ var rh1 = a1 || a7; // any || object is any >a7 : { a: string; } var rh2 = a2 || a7; // boolean || object is boolean | object ->rh2 : boolean | { a: string; } +>rh2 : true | { a: string; } >a2 || a7 : true | { a: string; } >a2 : boolean >a7 : { a: string; } @@ -462,7 +462,7 @@ var ri1 = a1 || a8; // any || array is any >a8 : string[] var ri2 = a2 || a8; // boolean || array is boolean | array ->ri2 : boolean | string[] +>ri2 : true | string[] >a2 || a8 : true | string[] >a2 : boolean >a8 : string[] @@ -522,7 +522,7 @@ var rj1 = a1 || null; // any || null is any >null : null var rj2 = a2 || null; // boolean || null is boolean ->rj2 : boolean +>rj2 : true >a2 || null : true >a2 : boolean >null : null @@ -582,7 +582,7 @@ var rf1 = a1 || undefined; // any || undefined is any >undefined : undefined var rf2 = a2 || undefined; // boolean || undefined is boolean ->rf2 : boolean +>rf2 : true >a2 || undefined : true >a2 : boolean >undefined : undefined diff --git a/tests/baselines/reference/numericLiteralTypes1.types b/tests/baselines/reference/numericLiteralTypes1.types index 435145557aff9..7556d0818ee45 100644 --- a/tests/baselines/reference/numericLiteralTypes1.types +++ b/tests/baselines/reference/numericLiteralTypes1.types @@ -371,13 +371,13 @@ function f15(x: 0 | false, y: 1 | "one") { >y : 1 | "one" var a = x && y; ->a : boolean | 0 +>a : false | 0 >x && y : false | 0 >x : false | 0 >y : 1 | "one" var b = y && x; ->b : boolean | 0 +>b : false | 0 >y && x : false | 0 >y : 1 | "one" >x : false | 0 @@ -389,7 +389,7 @@ function f15(x: 0 | false, y: 1 | "one") { >y : 1 | "one" var d = y || x; ->d : boolean | 0 | 1 | "one" +>d : false | 0 | 1 | "one" >y || x : false | 0 | 1 | "one" >y : 1 | "one" >x : false | 0 diff --git a/tests/baselines/reference/numericLiteralTypes2.types b/tests/baselines/reference/numericLiteralTypes2.types index 2e020f25ee831..32396d13da251 100644 --- a/tests/baselines/reference/numericLiteralTypes2.types +++ b/tests/baselines/reference/numericLiteralTypes2.types @@ -371,13 +371,13 @@ function f15(x: 0 | false, y: 1 | "one") { >y : 1 | "one" var a = x && y; ->a : boolean | 0 +>a : false | 0 >x && y : false | 0 >x : false | 0 >y : 1 | "one" var b = y && x; ->b : boolean | 0 +>b : false | 0 >y && x : false | 0 >y : 1 | "one" >x : false | 0 diff --git a/tests/baselines/reference/spreadBooleanRespectsFreshness.js b/tests/baselines/reference/spreadBooleanRespectsFreshness.js new file mode 100644 index 0000000000000..bb3ff104901d6 --- /dev/null +++ b/tests/baselines/reference/spreadBooleanRespectsFreshness.js @@ -0,0 +1,11 @@ +//// [spreadBooleanRespectsFreshness.ts] +type Foo = FooBase | FooArray; +type FooBase = string | false; +type FooArray = FooBase[]; + +declare let foo1: Foo; +declare let foo2: Foo; +foo1 = [...Array.isArray(foo2) ? foo2 : [foo2]]; + +//// [spreadBooleanRespectsFreshness.js] +foo1 = (Array.isArray(foo2) ? foo2 : [foo2]).slice(); diff --git a/tests/baselines/reference/spreadBooleanRespectsFreshness.symbols b/tests/baselines/reference/spreadBooleanRespectsFreshness.symbols new file mode 100644 index 0000000000000..90e9a7dace3d1 --- /dev/null +++ b/tests/baselines/reference/spreadBooleanRespectsFreshness.symbols @@ -0,0 +1,30 @@ +=== tests/cases/compiler/spreadBooleanRespectsFreshness.ts === +type Foo = FooBase | FooArray; +>Foo : Symbol(Foo, Decl(spreadBooleanRespectsFreshness.ts, 0, 0)) +>FooBase : Symbol(FooBase, Decl(spreadBooleanRespectsFreshness.ts, 0, 30)) +>FooArray : Symbol(FooArray, Decl(spreadBooleanRespectsFreshness.ts, 1, 30)) + +type FooBase = string | false; +>FooBase : Symbol(FooBase, Decl(spreadBooleanRespectsFreshness.ts, 0, 30)) + +type FooArray = FooBase[]; +>FooArray : Symbol(FooArray, Decl(spreadBooleanRespectsFreshness.ts, 1, 30)) +>FooBase : Symbol(FooBase, Decl(spreadBooleanRespectsFreshness.ts, 0, 30)) + +declare let foo1: Foo; +>foo1 : Symbol(foo1, Decl(spreadBooleanRespectsFreshness.ts, 4, 11)) +>Foo : Symbol(Foo, Decl(spreadBooleanRespectsFreshness.ts, 0, 0)) + +declare let foo2: Foo; +>foo2 : Symbol(foo2, Decl(spreadBooleanRespectsFreshness.ts, 5, 11)) +>Foo : Symbol(Foo, Decl(spreadBooleanRespectsFreshness.ts, 0, 0)) + +foo1 = [...Array.isArray(foo2) ? foo2 : [foo2]]; +>foo1 : Symbol(foo1, Decl(spreadBooleanRespectsFreshness.ts, 4, 11)) +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>foo2 : Symbol(foo2, Decl(spreadBooleanRespectsFreshness.ts, 5, 11)) +>foo2 : Symbol(foo2, Decl(spreadBooleanRespectsFreshness.ts, 5, 11)) +>foo2 : Symbol(foo2, Decl(spreadBooleanRespectsFreshness.ts, 5, 11)) + diff --git a/tests/baselines/reference/spreadBooleanRespectsFreshness.types b/tests/baselines/reference/spreadBooleanRespectsFreshness.types new file mode 100644 index 0000000000000..c607422b0629a --- /dev/null +++ b/tests/baselines/reference/spreadBooleanRespectsFreshness.types @@ -0,0 +1,32 @@ +=== tests/cases/compiler/spreadBooleanRespectsFreshness.ts === +type Foo = FooBase | FooArray; +>Foo : Foo + +type FooBase = string | false; +>FooBase : FooBase +>false : false + +type FooArray = FooBase[]; +>FooArray : FooBase[] + +declare let foo1: Foo; +>foo1 : Foo + +declare let foo2: Foo; +>foo2 : Foo + +foo1 = [...Array.isArray(foo2) ? foo2 : [foo2]]; +>foo1 = [...Array.isArray(foo2) ? foo2 : [foo2]] : FooBase[] +>foo1 : Foo +>[...Array.isArray(foo2) ? foo2 : [foo2]] : FooBase[] +>...Array.isArray(foo2) ? foo2 : [foo2] : FooBase +>Array.isArray(foo2) ? foo2 : [foo2] : FooBase[] +>Array.isArray(foo2) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>foo2 : Foo +>foo2 : FooBase[] +>[foo2] : FooBase[] +>foo2 : FooBase + diff --git a/tests/cases/compiler/spreadBooleanRespectsFreshness.ts b/tests/cases/compiler/spreadBooleanRespectsFreshness.ts new file mode 100644 index 0000000000000..ce9a58684b534 --- /dev/null +++ b/tests/cases/compiler/spreadBooleanRespectsFreshness.ts @@ -0,0 +1,7 @@ +type Foo = FooBase | FooArray; +type FooBase = string | false; +type FooArray = FooBase[]; + +declare let foo1: Foo; +declare let foo2: Foo; +foo1 = [...Array.isArray(foo2) ? foo2 : [foo2]]; \ No newline at end of file