diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c89f0083028ad..1421d31c26b2e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9853,7 +9853,7 @@ namespace ts { } function getPropertiesOfType(type: Type): Symbol[] { - type = getApparentType(getReducedType(type)); + type = getApparentType(type); return type.flags & TypeFlags.UnionOrIntersection ? getPropertiesOfUnionOrIntersectionType(type) : getPropertiesOfObjectType(type); @@ -10195,6 +10195,7 @@ namespace ts { * type itself. */ function getApparentType(type: Type): Type { + type = getReducedType(type); const t = type.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(type) || unknownType : type; return getObjectFlags(t) & ObjectFlags.Mapped ? getApparentTypeOfMappedType(t) : t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(t) : @@ -10352,7 +10353,7 @@ namespace ts { * no constituent property has type 'never', but the intersection of the constituent property types is 'never'. */ function getReducedType(type: Type): Type { - if (type.flags & TypeFlags.Union && (type).objectFlags & ObjectFlags.ContainsIntersections) { + if (type.flags & TypeFlags.Union && (type).objectFlags & ObjectFlags.ContainsReducibles) { return (type).resolvedReducedType || ((type).resolvedReducedType = getReducedUnionType(type)); } else if (type.flags & TypeFlags.Intersection) { @@ -10360,7 +10361,13 @@ namespace ts { (type).objectFlags |= ObjectFlags.IsNeverIntersectionComputed | (some(getPropertiesOfUnionOrIntersectionType(type), isDiscriminantWithNeverType) ? ObjectFlags.IsNeverIntersection : 0); } - return (type).objectFlags & ObjectFlags.IsNeverIntersection ? neverType : type; + if ((type).objectFlags & ObjectFlags.IsNeverIntersection) { + return neverType; + } + return (type).resolvedReducedType || ((type).resolvedReducedType = getReducedIntersectionType(type)); + } + else if (type.flags & TypeFlags.Conditional) { + return getReducedConditionalType(type as ConditionalType); } return type; } @@ -10377,6 +10384,18 @@ namespace ts { return reduced; } + function getReducedIntersectionType(type: IntersectionType) { + const reducedTypes = sameMap(type.types, getReducedType); + if (reducedTypes === type.types) { + return type; + } + const reduced = getIntersectionType(reducedTypes); + if (reduced.flags & TypeFlags.Intersection) { + (reduced).resolvedReducedType = reduced; + } + return reduced; + } + function isDiscriminantWithNeverType(prop: Symbol) { return !(prop.flags & SymbolFlags.Optional) && (getCheckFlags(prop) & (CheckFlags.Discriminant | CheckFlags.HasNeverType)) === CheckFlags.Discriminant && @@ -10430,7 +10449,7 @@ namespace ts { * maps primitive types and type parameters are to their apparent types. */ function getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[] { - return getSignaturesOfStructuredType(getApparentType(getReducedType(type)), kind); + return getSignaturesOfStructuredType(getApparentType(type), kind); } function getIndexInfoOfStructuredType(type: Type, kind: IndexKind): IndexInfo | undefined { @@ -10448,13 +10467,13 @@ namespace ts { // Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and // maps primitive types and type parameters are to their apparent types. function getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined { - return getIndexInfoOfStructuredType(getApparentType(getReducedType(type)), kind); + return getIndexInfoOfStructuredType(getApparentType(type), kind); } // Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and // maps primitive types and type parameters are to their apparent types. function getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined { - return getIndexTypeOfStructuredType(getApparentType(getReducedType(type)), kind); + return getIndexTypeOfStructuredType(getApparentType(type), kind); } function getImplicitIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined { @@ -12035,7 +12054,7 @@ namespace ts { } } const objectFlags = (includes & TypeFlags.NotPrimitiveUnion ? 0 : ObjectFlags.PrimitiveUnion) | - (includes & TypeFlags.Intersection ? ObjectFlags.ContainsIntersections : 0); + (includes & TypeFlags.ReducibleNotUnion ? ObjectFlags.ContainsReducibles : 0); return getUnionTypeFromSortedList(typeSet, objectFlags, aliasSymbol, aliasTypeArguments); } @@ -12750,6 +12769,25 @@ namespace ts { return type[cache] = type; } + function getReducedConditionalType(type: ConditionalType): Type { + const checkType = type.checkType; + const extendsType = getInferredExtendsTypeOfConditional(type); + + if (!isGenericObjectType(checkType) && !isGenericIndexType(checkType) && !isGenericObjectType(extendsType) && !isGenericIndexType(extendsType)) { + const result = getConditionalSimplificationState(checkType, extendsType); + switch (result) { + case ConditionalSimplificationState.True: + return getReducedType(getInferredTrueTypeFromConditionalType(type)); + case ConditionalSimplificationState.False: + return getReducedType(getFalseTypeFromConditionalType(type)); + case ConditionalSimplificationState.Both: + return getUnionType([getReducedType(getInferredTrueTypeFromConditionalType(type)), getReducedType(getFalseTypeFromConditionalType(type))]); + // None: Fall out and return `type` + } + } + return type; + } + function getSimplifiedConditionalType(type: ConditionalType, writing: boolean) { const checkType = type.checkType; const extendsType = type.extendsType; @@ -12821,7 +12859,7 @@ namespace ts { // In the following we resolve T[K] to the type of the property in T selected by K. // We treat boolean as different from other unions to improve errors; // skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'. - const apparentObjectType = getApparentType(getReducedType(objectType)); + const apparentObjectType = getApparentType(objectType); if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Boolean)) { const propTypes: Type[] = []; let wasMissingProp = false; @@ -12888,6 +12926,14 @@ namespace ts { return type; } + function isTypeDeferredTypeReference(type: Type) { + return !!(getObjectFlags(type) & ObjectFlags.Reference) && !!(type as TypeReference).node; + } + + function getInferredExtendsTypeOfConditional(type: ConditionalType) { + return instantiateType(type.root.extendsType, type.combinedMapper || type.mapper); + } + function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined): Type { const checkType = instantiateType(root.checkType, mapper); const extendsType = instantiateType(root.extendsType, mapper); @@ -12913,28 +12959,17 @@ namespace ts { // Instantiate the extends type including inferences for 'infer T' type parameters const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType; // We attempt to resolve the conditional type only when the check and extends types are non-generic - if (!checkTypeInstantiable && !isGenericObjectType(inferredExtendsType) && !isGenericIndexType(inferredExtendsType)) { - if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { - return instantiateType(root.trueType, combinedMapper || mapper); - } - // Return union of trueType and falseType for 'any' since it matches anything - if (checkType.flags & TypeFlags.Any) { - return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]); - } - // Return falseType for a definitely false extends check. We check an instantiations of the two - // types with type parameters mapped to the wildcard type, the most permissive instantiations - // possible (the wildcard type is assignable to and from all types). If those are not related, - // then no instantiations will be and we can just return the false branch type. - if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { - return instantiateType(root.falseType, mapper); - } - // Return trueType for a definitely true extends check. We check instantiations of the two - // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter - // that has no constraint. This ensures that, for example, the type - // type Foo = T extends { x: string } ? string : number - // doesn't immediately resolve to 'string' instead of being deferred. - if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { - return instantiateType(root.trueType, combinedMapper || mapper); + if (!checkTypeInstantiable && !isGenericObjectType(inferredExtendsType) && !isGenericIndexType(inferredExtendsType) + && !isTypeDeferredTypeReference(checkType) && !isTypeDeferredTypeReference(inferredExtendsType)) { + const result = getConditionalSimplificationState(checkType, inferredExtendsType); + switch (result) { + case ConditionalSimplificationState.True: + return instantiateType(root.trueType, combinedMapper || mapper); + case ConditionalSimplificationState.Both: + return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]); + case ConditionalSimplificationState.False: + return instantiateType(root.falseType, mapper); + // None: Fall out and defer } } // Return a deferred type for a check that is neither definitely true nor definitely false @@ -12950,6 +12985,39 @@ namespace ts { return result; } + const enum ConditionalSimplificationState { + None = 0, + True = 1, + False = 2, + Both = True | False, + } + + function getConditionalSimplificationState(checkType: Type, inferredExtendsType: Type): ConditionalSimplificationState { + if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { + return ConditionalSimplificationState.True; + } + // Return union of trueType and falseType for 'any' since it matches anything + if (checkType.flags & TypeFlags.Any) { + return ConditionalSimplificationState.Both; + } + // Return falseType for a definitely false extends check. We check an instantiations of the two + // types with type parameters mapped to the wildcard type, the most permissive instantiations + // possible (the wildcard type is assignable to and from all types). If those are not related, + // then no instantiations will be and we can just return the false branch type. + if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { + return ConditionalSimplificationState.False; + } + // Return trueType for a definitely true extends check. We check instantiations of the two + // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter + // that has no constraint. This ensures that, for example, the type + // type Foo = T extends { x: string } ? string : number + // doesn't immediately resolve to 'string' instead of being deferred. + if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { + return ConditionalSimplificationState.True; + } + return ConditionalSimplificationState.None; + } + function getTrueTypeFromConditionalType(type: ConditionalType) { return type.resolvedTrueType || (type.resolvedTrueType = instantiateType(type.root.trueType, type.mapper)); } @@ -14986,7 +15054,7 @@ namespace ts { while (true) { const t = isFreshLiteralType(type) ? (type).regularType : getObjectFlags(type) & ObjectFlags.Reference && (type).node ? createTypeReference((type).target, getTypeArguments(type)) : - type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) : + type.flags & TypeFlags.Reducible ? getSimplifiedType(getReducedType(type), writing) : type.flags & TypeFlags.Substitution ? writing ? (type).baseType : (type).substitute : type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : type; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6543d8904f604..4e6befddce00a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4376,6 +4376,10 @@ namespace ts { /* @internal */ Simplifiable = IndexedAccess | Conditional, /* @internal */ + ReducibleNotUnion = Intersection | Conditional, + /* @internal */ + Reducible = ReducibleNotUnion | Union, + /* @internal */ Substructure = Object | Union | Intersection | Index | IndexedAccess | Conditional | Substitution, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never @@ -4385,7 +4389,7 @@ namespace ts { NotPrimitiveUnion = Any | Unknown | Enum | Void | Never | StructuredOrInstantiable, // The following flags are aggregated during union and intersection type construction /* @internal */ - IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive, + IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | Conditional, // The following flags are used for different purposes during union and intersection type construction /* @internal */ IncludesStructuredOrInstantiable = TypeParameter, @@ -4394,7 +4398,7 @@ namespace ts { /* @internal */ IncludesWildcard = IndexedAccess, /* @internal */ - IncludesEmptyObject = Conditional, + IncludesEmptyObject = 1 << 27, } export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; @@ -4511,7 +4515,7 @@ namespace ts { /* @internal */ CouldContainTypeVariables = 1 << 27, // Type could contain a type variable /* @internal */ - ContainsIntersections = 1 << 28, // Union contains intersections + ContainsReducibles = 1 << 28, // Union contains intersections /* @internal */ IsNeverIntersectionComputed = 1 << 28, // IsNeverLike flag has been computed /* @internal */ @@ -4634,11 +4638,11 @@ namespace ts { resolvedStringIndexType: IndexType; /* @internal */ resolvedBaseConstraint: Type; + /* @internal */ + resolvedReducedType: Type; } export interface UnionType extends UnionOrIntersectionType { - /* @internal */ - resolvedReducedType: Type; } export interface IntersectionType extends UnionOrIntersectionType { diff --git a/tests/baselines/reference/recursiveArrayNotCircular.js b/tests/baselines/reference/recursiveArrayNotCircular.js new file mode 100644 index 0000000000000..50f612bfcbf9c --- /dev/null +++ b/tests/baselines/reference/recursiveArrayNotCircular.js @@ -0,0 +1,66 @@ +//// [recursiveArrayNotCircular.ts] +type Action = P extends void ? { type : T } : { type: T, payload: P } + +enum ActionType { + Foo, + Bar, + Baz, + Batch +} + +type ReducerAction = + | Action + | Action + | Action + | Action + +function assertNever(a: never): never { + throw new Error("Unreachable!"); +} + +function reducer(action: ReducerAction): void { + switch(action.type) { + case ActionType.Bar: + const x: number = action.payload; + break; + case ActionType.Baz: + const y: boolean = action.payload; + break; + case ActionType.Foo: + const z: string = action.payload; + break; + case ActionType.Batch: + action.payload.map(reducer); + break; + default: return assertNever(action); + } +} + +//// [recursiveArrayNotCircular.js] +var ActionType; +(function (ActionType) { + ActionType[ActionType["Foo"] = 0] = "Foo"; + ActionType[ActionType["Bar"] = 1] = "Bar"; + ActionType[ActionType["Baz"] = 2] = "Baz"; + ActionType[ActionType["Batch"] = 3] = "Batch"; +})(ActionType || (ActionType = {})); +function assertNever(a) { + throw new Error("Unreachable!"); +} +function reducer(action) { + switch (action.type) { + case ActionType.Bar: + var x = action.payload; + break; + case ActionType.Baz: + var y = action.payload; + break; + case ActionType.Foo: + var z = action.payload; + break; + case ActionType.Batch: + action.payload.map(reducer); + break; + default: return assertNever(action); + } +} diff --git a/tests/baselines/reference/recursiveArrayNotCircular.symbols b/tests/baselines/reference/recursiveArrayNotCircular.symbols new file mode 100644 index 0000000000000..83a1610e9f440 --- /dev/null +++ b/tests/baselines/reference/recursiveArrayNotCircular.symbols @@ -0,0 +1,126 @@ +=== tests/cases/compiler/recursiveArrayNotCircular.ts === +type Action = P extends void ? { type : T } : { type: T, payload: P } +>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12)) +>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14)) +>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14)) +>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 38)) +>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12)) +>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53)) +>T : Symbol(T, Decl(recursiveArrayNotCircular.ts, 0, 12)) +>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62)) +>P : Symbol(P, Decl(recursiveArrayNotCircular.ts, 0, 14)) + +enum ActionType { +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) + + Foo, +>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17)) + + Bar, +>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8)) + + Baz, +>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8)) + + Batch +>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8)) +} + +type ReducerAction = +>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1)) + + | Action +>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8)) + + | Action +>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8)) + + | Action +>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17)) + + | Action +>Action : Symbol(Action, Decl(recursiveArrayNotCircular.ts, 0, 0)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8)) +>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1)) + +function assertNever(a: never): never { +>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45)) +>a : Symbol(a, Decl(recursiveArrayNotCircular.ts, 15, 21)) + + throw new Error("Unreachable!"); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +} + +function reducer(action: ReducerAction): void { +>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1)) +>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17)) +>ReducerAction : Symbol(ReducerAction, Decl(recursiveArrayNotCircular.ts, 7, 1)) + + switch(action.type) { +>action.type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53)) +>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17)) +>type : Symbol(type, Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53), Decl(recursiveArrayNotCircular.ts, 0, 53)) + + case ActionType.Bar: +>ActionType.Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Bar : Symbol(ActionType.Bar, Decl(recursiveArrayNotCircular.ts, 3, 8)) + + const x: number = action.payload; +>x : Symbol(x, Decl(recursiveArrayNotCircular.ts, 22, 17)) +>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62)) +>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17)) +>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62)) + + break; + case ActionType.Baz: +>ActionType.Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Baz : Symbol(ActionType.Baz, Decl(recursiveArrayNotCircular.ts, 4, 8)) + + const y: boolean = action.payload; +>y : Symbol(y, Decl(recursiveArrayNotCircular.ts, 25, 17)) +>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62)) +>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17)) +>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62), Decl(recursiveArrayNotCircular.ts, 0, 62)) + + break; + case ActionType.Foo: +>ActionType.Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Foo : Symbol(ActionType.Foo, Decl(recursiveArrayNotCircular.ts, 2, 17)) + + const z: string = action.payload; +>z : Symbol(z, Decl(recursiveArrayNotCircular.ts, 28, 17)) +>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62)) +>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17)) +>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62)) + + break; + case ActionType.Batch: +>ActionType.Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8)) +>ActionType : Symbol(ActionType, Decl(recursiveArrayNotCircular.ts, 0, 75)) +>Batch : Symbol(ActionType.Batch, Decl(recursiveArrayNotCircular.ts, 5, 8)) + + action.payload.map(reducer); +>action.payload.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>action.payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62)) +>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17)) +>payload : Symbol(payload, Decl(recursiveArrayNotCircular.ts, 0, 62)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>reducer : Symbol(reducer, Decl(recursiveArrayNotCircular.ts, 17, 1)) + + break; + default: return assertNever(action); +>assertNever : Symbol(assertNever, Decl(recursiveArrayNotCircular.ts, 13, 45)) +>action : Symbol(action, Decl(recursiveArrayNotCircular.ts, 19, 17)) + } +} diff --git a/tests/baselines/reference/recursiveArrayNotCircular.types b/tests/baselines/reference/recursiveArrayNotCircular.types new file mode 100644 index 0000000000000..bf532f03c5dab --- /dev/null +++ b/tests/baselines/reference/recursiveArrayNotCircular.types @@ -0,0 +1,114 @@ +=== tests/cases/compiler/recursiveArrayNotCircular.ts === +type Action = P extends void ? { type : T } : { type: T, payload: P } +>Action : Action +>type : T +>type : T +>payload : P + +enum ActionType { +>ActionType : ActionType + + Foo, +>Foo : ActionType.Foo + + Bar, +>Bar : ActionType.Bar + + Baz, +>Baz : ActionType.Baz + + Batch +>Batch : ActionType.Batch +} + +type ReducerAction = +>ReducerAction : { type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; } + + | Action +>ActionType : any + + | Action +>ActionType : any + + | Action +>ActionType : any + + | Action +>ActionType : any + +function assertNever(a: never): never { +>assertNever : (a: never) => never +>a : never + + throw new Error("Unreachable!"); +>new Error("Unreachable!") : Error +>Error : ErrorConstructor +>"Unreachable!" : "Unreachable!" +} + +function reducer(action: ReducerAction): void { +>reducer : (action: { type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; }) => void +>action : { type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; } + + switch(action.type) { +>action.type : ActionType +>action : { type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; } +>type : ActionType + + case ActionType.Bar: +>ActionType.Bar : ActionType.Bar +>ActionType : typeof ActionType +>Bar : ActionType.Bar + + const x: number = action.payload; +>x : number +>action.payload : number +>action : { type: ActionType.Bar; payload: number; } +>payload : number + + break; + case ActionType.Baz: +>ActionType.Baz : ActionType.Baz +>ActionType : typeof ActionType +>Baz : ActionType.Baz + + const y: boolean = action.payload; +>y : boolean +>action.payload : boolean +>action : { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } +>payload : boolean + + break; + case ActionType.Foo: +>ActionType.Foo : ActionType.Foo +>ActionType : typeof ActionType +>Foo : ActionType.Foo + + const z: string = action.payload; +>z : string +>action.payload : string +>action : { type: ActionType.Foo; payload: string; } +>payload : string + + break; + case ActionType.Batch: +>ActionType.Batch : ActionType.Batch +>ActionType : typeof ActionType +>Batch : ActionType.Batch + + action.payload.map(reducer); +>action.payload.map(reducer) : void[] +>action.payload.map : (callbackfn: (value: { type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; }, index: number, array: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; })[]) => U, thisArg?: any) => U[] +>action.payload : ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; })[] +>action : { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; } +>payload : ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; })[] +>map : (callbackfn: (value: { type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; }, index: number, array: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; })[]) => U, thisArg?: any) => U[] +>reducer : (action: { type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | { type: ActionType.Batch; payload: ({ type: ActionType.Bar; payload: number; } | { type: ActionType.Baz; payload: false; } | { type: ActionType.Baz; payload: true; } | { type: ActionType.Foo; payload: string; } | any)[]; }) => void + + break; + default: return assertNever(action); +>assertNever(action) : never +>assertNever : (a: never) => never +>action : never + } +} diff --git a/tests/cases/compiler/recursiveArrayNotCircular.ts b/tests/cases/compiler/recursiveArrayNotCircular.ts new file mode 100644 index 0000000000000..3bf2b419f3ae0 --- /dev/null +++ b/tests/cases/compiler/recursiveArrayNotCircular.ts @@ -0,0 +1,36 @@ +type Action = P extends void ? { type : T } : { type: T, payload: P } + +enum ActionType { + Foo, + Bar, + Baz, + Batch +} + +type ReducerAction = + | Action + | Action + | Action + | Action + +function assertNever(a: never): never { + throw new Error("Unreachable!"); +} + +function reducer(action: ReducerAction): void { + switch(action.type) { + case ActionType.Bar: + const x: number = action.payload; + break; + case ActionType.Baz: + const y: boolean = action.payload; + break; + case ActionType.Foo: + const z: string = action.payload; + break; + case ActionType.Batch: + action.payload.map(reducer); + break; + default: return assertNever(action); + } +} \ No newline at end of file