diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2444c5ab1bcdb..612cb82d0571d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -299,6 +299,7 @@ namespace ts { let instantiationCount = 0; let instantiationDepth = 0; let constraintDepth = 0; + let mapperIdCount = 0; let currentNode: Node | undefined; const emptySymbols = createSymbolTable(); @@ -5028,6 +5029,17 @@ namespace ts { return true; } + /** + * Checks the circularity stack like `pushTypeResolution` without making any edits + */ + function peekTypeResolution(target: TypeSystemEntity, propertyName: TypeSystemPropertyName) { + const resolutionCycleStartIndex = findResolutionCycleStartIndex(target, propertyName); + if (resolutionCycleStartIndex >= 0) { + return false; + } + return true; + } + function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number { for (let i = resolutionTargets.length - 1; i >= 0; i--) { if (hasType(resolutionTargets[i], resolutionPropertyNames[i])) { @@ -6531,6 +6543,12 @@ namespace ts { // If typeNode is missing, we will error in checkJSDocTypedefTag. let type = typeNode ? getTypeFromTypeNode(typeNode) : errorType; + if (type.flags & TypeFlags.Substitution) { + // Trigger resolution of any deferred substiutes that are directly assigned to the alias so they + // are marked as circular and the alias becomes `any` + void (type as SubstitutionType).substitute; // tslint:disable-line:no-unused-expression + } + if (popTypeResolution()) { const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); if (typeParameters) { @@ -9145,6 +9163,18 @@ namespace ts { * declared type. Instantiations are cached using the type identities of the type arguments as the key. */ function getTypeFromTypeAliasReference(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type { + if (!peekTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) { + const links = getNodeLinks(node); + let cb = links.substituteCallback; + if (!cb) { + cb = links.substituteCallback = () => getTypeFromTypeAliasReferenceWorker(node, symbol, typeArguments); + } + return getDeferredSubstitutionType(cb, symbol, typeArguments); + } + return getTypeFromTypeAliasReferenceWorker(node, symbol, typeArguments); + } + + function getTypeFromTypeAliasReferenceWorker(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined) { const type = getDeclaredTypeOfSymbol(symbol); const typeParameters = getSymbolLinks(symbol).typeParameters; if (typeParameters) { @@ -9284,6 +9314,33 @@ namespace ts { return result; } + /** + * Manufactures a substitute whose typeVariable/substitute are the same type, + * and whose values are not generated until accessed. + */ + function getDeferredSubstitutionType(cb: DeferredSubsCallback, aliasSymbol: Symbol, aliasTypeArguments: readonly Type[] | undefined) { + const id = cb.deferredSubsId; + if (id) { + return substitutionTypes.get(id)!; + } + const result = createType(TypeFlags.Substitution); + result.aliasSymbol = aliasSymbol; // Yep - a substitute with a type alias. Without this, a bunch of anonymous self referential types would probably blow up in printback + result.aliasTypeArguments = aliasTypeArguments; + let cached: Type; + Object.defineProperty(result, "typeVariable", { + get() { + return cached || (cached = cb()); + } + }); + Object.defineProperty(result, "substitute", { + get() { + return cached || (cached = cb()); + } + }); + substitutionTypes.set(cb.deferredSubsId = "" + getTypeId(result), result); + return result; + } + function isUnaryTupleTypeNode(node: TypeNode) { return node.kind === SyntaxKind.TupleType && (node).elementTypes.length === 1; } @@ -11643,6 +11700,10 @@ namespace ts { return result; } + function getTypeMapperId(mapper: TypeMapper) { + return mapper.id || (mapper.id = ++mapperIdCount); + } + function instantiateTypeWorker(type: Type, mapper: TypeMapper): Type { const flags = type.flags; if (flags & TypeFlags.TypeParameter) { @@ -11687,16 +11748,28 @@ namespace ts { return getConditionalTypeInstantiation(type, combineTypeMappers((type).mapper, mapper)); } if (flags & TypeFlags.Substitution) { - const maybeVariable = instantiateType((type).typeVariable, mapper); + const sub = (type as SubstitutionType); + if (substitutionTypes.has("" + getTypeId(sub))) { + // substitution is deferred - wrap it + const mapperId = "" + getTypeMapperId(mapper); + let cb = sub.deferredInstantiationCallbacks && sub.deferredInstantiationCallbacks.get(mapperId); + if (!cb) { + cb = () => instantiateType(sub.substitute, mapper); + (sub.deferredInstantiationCallbacks || (sub.deferredInstantiationCallbacks = createMap())).set(mapperId, cb); + } + + return getDeferredSubstitutionType(cb, sub.aliasSymbol!, instantiateTypes(sub.aliasTypeArguments, mapper)); + } + const maybeVariable = instantiateType(sub.typeVariable, mapper); if (maybeVariable.flags & TypeFlags.TypeVariable) { - return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((type).substitute, mapper)); + return getSubstitutionType(maybeVariable as TypeVariable, instantiateType(sub.substitute, mapper)); } else { - const sub = instantiateType((type).substitute, mapper); - if (sub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) { + const newSub = instantiateType(sub.substitute, mapper); + if (newSub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(newSub))) { return maybeVariable; } - return sub; + return newSub; } } return type; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5e6f36346e3f7..50acc99cda64c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3953,6 +3953,13 @@ namespace ts { contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive deferredNodes?: Map; // Set of nodes whose checking has been deferred capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement + substituteCallback?: DeferredSubsCallback; // For a type alias reference, the callback manufactured to create the reference to the alias in a deferred way + } + + /* @internal */ + export interface DeferredSubsCallback { + (): Type; + deferredSubsId?: string; } export const enum TypeFlags { @@ -4428,6 +4435,8 @@ namespace ts { export interface SubstitutionType extends InstantiableType { typeVariable: TypeVariable; // Target type variable substitute: Type; // Type to substitute for type parameter + /* @internal */ + deferredInstantiationCallbacks?: Map; // list of callbacks used to instantiate in a deferred way - stored to cache } /* @internal */ @@ -4490,7 +4499,10 @@ namespace ts { } /* @internal */ - export type TypeMapper = (t: TypeParameter) => Type; + export interface TypeMapper { + (t: TypeParameter): Type; + id?: number; // lazily assigned + } export const enum InferencePriority { NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type diff --git a/tests/baselines/reference/circularTypeofWithVarOrFunc.errors.txt b/tests/baselines/reference/circularTypeofWithVarOrFunc.errors.txt index 1fb989b75f5c1..e659bb607623b 100644 --- a/tests/baselines/reference/circularTypeofWithVarOrFunc.errors.txt +++ b/tests/baselines/reference/circularTypeofWithVarOrFunc.errors.txt @@ -1,8 +1,6 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(1,6): error TS2456: Type alias 'typeAlias1' circularly references itself. tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(2,5): error TS2502: 'varOfAliasedType1' is referenced directly or indirectly in its own type annotation. -tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(4,5): error TS2502: 'varOfAliasedType2' is referenced directly or indirectly in its own type annotation. tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(5,6): error TS2456: Type alias 'typeAlias2' circularly references itself. -tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(7,18): error TS2577: Return type annotation circularly references itself. tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(9,6): error TS2456: Type alias 'typeAlias3' circularly references itself. tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(18,6): error TS2456: Type alias 'R' circularly references itself. tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(19,29): error TS2577: Return type annotation circularly references itself. @@ -10,7 +8,7 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarO tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts(26,15): error TS2577: Return type annotation circularly references itself. -==== tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts (10 errors) ==== +==== tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarOrFunc.ts (8 errors) ==== type typeAlias1 = typeof varOfAliasedType1; ~~~~~~~~~~ !!! error TS2456: Type alias 'typeAlias1' circularly references itself. @@ -19,15 +17,11 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/circularTypeofWithVarO !!! error TS2502: 'varOfAliasedType1' is referenced directly or indirectly in its own type annotation. var varOfAliasedType2: typeAlias2; - ~~~~~~~~~~~~~~~~~ -!!! error TS2502: 'varOfAliasedType2' is referenced directly or indirectly in its own type annotation. type typeAlias2 = typeof varOfAliasedType2; ~~~~~~~~~~ !!! error TS2456: Type alias 'typeAlias2' circularly references itself. function func(): typeAlias3 { return null; } - ~~~~~~~~~~ -!!! error TS2577: Return type annotation circularly references itself. var varOfAliasedType3 = func(); type typeAlias3 = typeof varOfAliasedType3; ~~~~~~~~~~ diff --git a/tests/baselines/reference/circularTypeofWithVarOrFunc.types b/tests/baselines/reference/circularTypeofWithVarOrFunc.types index c2c8ba7947fb5..7684cfe065836 100644 --- a/tests/baselines/reference/circularTypeofWithVarOrFunc.types +++ b/tests/baselines/reference/circularTypeofWithVarOrFunc.types @@ -7,24 +7,24 @@ var varOfAliasedType1: typeAlias1; >varOfAliasedType1 : any var varOfAliasedType2: typeAlias2; ->varOfAliasedType2 : any +>varOfAliasedType2 : typeAlias2 type typeAlias2 = typeof varOfAliasedType2; >typeAlias2 : any ->varOfAliasedType2 : any +>varOfAliasedType2 : typeAlias2 function func(): typeAlias3 { return null; } ->func : () => any +>func : () => typeAlias3 >null : null var varOfAliasedType3 = func(); ->varOfAliasedType3 : any ->func() : any ->func : () => any +>varOfAliasedType3 : typeAlias3 +>func() : typeAlias3 +>func : () => typeAlias3 type typeAlias3 = typeof varOfAliasedType3; >typeAlias3 : any ->varOfAliasedType3 : any +>varOfAliasedType3 : typeAlias3 // Repro from #26104 diff --git a/tests/baselines/reference/directDependenceBetweenTypeAliases.errors.txt b/tests/baselines/reference/directDependenceBetweenTypeAliases.errors.txt index 68ea92f8fb559..0628a016e3099 100644 --- a/tests/baselines/reference/directDependenceBetweenTypeAliases.errors.txt +++ b/tests/baselines/reference/directDependenceBetweenTypeAliases.errors.txt @@ -2,20 +2,9 @@ tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts( tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(5,6): error TS2456: Type alias 'T0_1' circularly references itself. tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(6,6): error TS2456: Type alias 'T0_2' circularly references itself. tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(7,6): error TS2456: Type alias 'T0_3' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(11,6): error TS2456: Type alias 'T1' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(14,6): error TS2456: Type alias 'T2' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(16,6): error TS2456: Type alias 'T2_1' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(19,6): error TS2456: Type alias 'T3' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(22,6): error TS2456: Type alias 'T4' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(25,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(26,6): error TS2456: Type alias 'T5' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(29,6): error TS2456: Type alias 'T6' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(30,6): error TS2456: Type alias 'T7' circularly references itself. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(31,5): error TS2502: 'yy' is referenced directly or indirectly in its own type annotation. -tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts(32,6): error TS2456: Type alias 'T8' circularly references itself. -==== tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts (15 errors) ==== +==== tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts (4 errors) ==== // It is an error for the type specified in a type alias to depend on that type alias // A type alias directly depends on the type it aliases. @@ -35,49 +24,27 @@ tests/cases/conformance/types/typeAliases/directDependenceBetweenTypeAliases.ts( // A type reference directly depends on the referenced type and each of the type arguments, if any. interface I {} type T1 = I - ~~ -!!! error TS2456: Type alias 'T1' circularly references itself. // A union type directly depends on each of the constituent types. type T2 = T2 | string - ~~ -!!! error TS2456: Type alias 'T2' circularly references itself. class C {} type T2_1 = T2_1[] | number - ~~~~ -!!! error TS2456: Type alias 'T2_1' circularly references itself. // An array type directly depends on its element type. type T3 = T3[] - ~~ -!!! error TS2456: Type alias 'T3' circularly references itself. // A tuple type directly depends on each of its element types. type T4 = [number, T4] - ~~ -!!! error TS2456: Type alias 'T4' circularly references itself. // A type query directly depends on the type of the referenced entity. var x: T5[] = [] - ~ -!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation. type T5 = typeof x - ~~ -!!! error TS2456: Type alias 'T5' circularly references itself. class C1 {} type T6 = T7 | number - ~~ -!!! error TS2456: Type alias 'T6' circularly references itself. type T7 = typeof yy - ~~ -!!! error TS2456: Type alias 'T7' circularly references itself. var yy: [string, T8[]]; - ~~ -!!! error TS2502: 'yy' is referenced directly or indirectly in its own type annotation. type T8 = C - ~~ -!!! error TS2456: Type alias 'T8' circularly references itself. // legal cases type T9 = () => T9 diff --git a/tests/baselines/reference/directDependenceBetweenTypeAliases.types b/tests/baselines/reference/directDependenceBetweenTypeAliases.types index 3363b20b85199..fe0533c668a09 100644 --- a/tests/baselines/reference/directDependenceBetweenTypeAliases.types +++ b/tests/baselines/reference/directDependenceBetweenTypeAliases.types @@ -17,50 +17,50 @@ type T0_3 = T0_1 // A type reference directly depends on the referenced type and each of the type arguments, if any. interface I {} type T1 = I ->T1 : any +>T1 : I // A union type directly depends on each of the constituent types. type T2 = T2 | string ->T2 : any +>T2 : T2 class C {} >C : C type T2_1 = T2_1[] | number ->T2_1 : any +>T2_1 : T2_1 // An array type directly depends on its element type. type T3 = T3[] ->T3 : any +>T3 : T3[] // A tuple type directly depends on each of its element types. type T4 = [number, T4] ->T4 : any +>T4 : [number, T4] // A type query directly depends on the type of the referenced entity. var x: T5[] = [] ->x : any +>x : T5[] >[] : undefined[] type T5 = typeof x ->T5 : any ->x : any +>T5 : T5[] +>x : T5[] class C1 {} >C1 : C1 type T6 = T7 | number ->T6 : any +>T6 : T6 type T7 = typeof yy ->T7 : any ->yy : any +>T7 : [string, C[]] +>yy : [string, C[]] var yy: [string, T8[]]; ->yy : any +>yy : [string, C[]] type T8 = C ->T8 : any +>T8 : C // legal cases type T9 = () => T9 diff --git a/tests/baselines/reference/recursiveMappedTypes.errors.txt b/tests/baselines/reference/recursiveMappedTypes.errors.txt index 43a0e736d256f..b6d7c8eeb8914 100644 --- a/tests/baselines/reference/recursiveMappedTypes.errors.txt +++ b/tests/baselines/reference/recursiveMappedTypes.errors.txt @@ -1,39 +1,20 @@ -tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(3,6): error TS2456: Type alias 'Recurse' circularly references itself. -tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(4,11): error TS2313: Type parameter 'K' has a circular constraint. -tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(7,6): error TS2456: Type alias 'Recurse1' circularly references itself. -tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(8,11): error TS2313: Type parameter 'K' has a circular constraint. -tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(11,6): error TS2456: Type alias 'Recurse2' circularly references itself. -tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(12,11): error TS2313: Type parameter 'K' has a circular constraint. tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(20,19): error TS2589: Type instantiation is excessively deep and possibly infinite. tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(66,25): error TS2313: Type parameter 'P' has a circular constraint. -==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (8 errors) ==== +==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (2 errors) ==== // Recursive mapped types simply appear empty type Recurse = { - ~~~~~~~ -!!! error TS2456: Type alias 'Recurse' circularly references itself. [K in keyof Recurse]: Recurse[K] - ~~~~~~~~~~~~~ -!!! error TS2313: Type parameter 'K' has a circular constraint. } type Recurse1 = { - ~~~~~~~~ -!!! error TS2456: Type alias 'Recurse1' circularly references itself. [K in keyof Recurse2]: Recurse2[K] - ~~~~~~~~~~~~~~ -!!! error TS2313: Type parameter 'K' has a circular constraint. } type Recurse2 = { - ~~~~~~~~ -!!! error TS2456: Type alias 'Recurse2' circularly references itself. [K in keyof Recurse1]: Recurse1[K] - ~~~~~~~~~~~~~~ -!!! error TS2313: Type parameter 'K' has a circular constraint. -!!! related TS2751 tests/cases/conformance/types/mapped/recursiveMappedTypes.ts:8:17: Circularity originates in type at this location. } // Repro from #27881 diff --git a/tests/baselines/reference/recursiveMappedTypes.types b/tests/baselines/reference/recursiveMappedTypes.types index 34cfd2d610062..b617c53217088 100644 --- a/tests/baselines/reference/recursiveMappedTypes.types +++ b/tests/baselines/reference/recursiveMappedTypes.types @@ -2,19 +2,19 @@ // Recursive mapped types simply appear empty type Recurse = { ->Recurse : any +>Recurse : Recurse [K in keyof Recurse]: Recurse[K] } type Recurse1 = { ->Recurse1 : any +>Recurse1 : Recurse1 [K in keyof Recurse2]: Recurse2[K] } type Recurse2 = { ->Recurse2 : any +>Recurse2 : Recurse2 [K in keyof Recurse1]: Recurse1[K] } diff --git a/tests/baselines/reference/selfReferentialTypeAliases.errors.txt b/tests/baselines/reference/selfReferentialTypeAliases.errors.txt new file mode 100644 index 0000000000000..c80a718353164 --- /dev/null +++ b/tests/baselines/reference/selfReferentialTypeAliases.errors.txt @@ -0,0 +1,37 @@ +tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts(20,26): error TS2322: Type 'string' is not assignable to type '[string | number, Alternating?]'. +tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts(21,28): error TS2741: Property '0' is missing in type '(number | (string | (string | number)[])[])[]' but required in type '[string | number, Alternating?]'. +tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts(22,26): error TS2741: Property '0' is missing in type 'number[]' but required in type '[string, Alternating?]'. + + +==== tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts (3 errors) ==== + type HypertextNode = string | [string, { [key: string]: any }, ...HypertextNode[]]; + + const hypertextNode: HypertextNode = + ["div", { id: "parent" }, + ["div", { id: "first-child" }, "I'm the first child"], + ["div", { id: "second-child" }, "I'm the second child"] + ]; + + type Alternating = [T, Alternating?]; + + declare function reparam(x: Alternating): T; + + // inference for this alternating reference pattern is.... interesting. + const re1 = reparam([12]); + const re2 = reparam(["ok"]); + const re3 = reparam([12, ["ok"]]); + const re4 = reparam(["ok", [12]]); + const re5 = reparam([12, ["ok", [0]]]); + const re6 = reparam(["ok", [12, ["k"]]]); + const re7 = reparam([12, "not ok"]); // arity error + ~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type '[string | number, Alternating?]'. + const re8 = reparam(["ok", [12, ["ok", [12, "not ok"]]]]); // deep arity error + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2741: Property '0' is missing in type '(number | (string | (string | number)[])[])[]' but required in type '[string | number, Alternating?]'. + const re9 = reparam([12, [12]]); // non-alternating + ~~~~ +!!! error TS2741: Property '0' is missing in type 'number[]' but required in type '[string, Alternating?]'. + const re10 = reparam(["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]]); // deep non-alternating - we should strive to issue an error here, I think, but we infer `string | number` for T and do not + + \ No newline at end of file diff --git a/tests/baselines/reference/selfReferentialTypeAliases.js b/tests/baselines/reference/selfReferentialTypeAliases.js new file mode 100644 index 0000000000000..ad00a417211a2 --- /dev/null +++ b/tests/baselines/reference/selfReferentialTypeAliases.js @@ -0,0 +1,43 @@ +//// [selfReferentialTypeAliases.ts] +type HypertextNode = string | [string, { [key: string]: any }, ...HypertextNode[]]; + +const hypertextNode: HypertextNode = + ["div", { id: "parent" }, + ["div", { id: "first-child" }, "I'm the first child"], + ["div", { id: "second-child" }, "I'm the second child"] + ]; + +type Alternating = [T, Alternating?]; + +declare function reparam(x: Alternating): T; + +// inference for this alternating reference pattern is.... interesting. +const re1 = reparam([12]); +const re2 = reparam(["ok"]); +const re3 = reparam([12, ["ok"]]); +const re4 = reparam(["ok", [12]]); +const re5 = reparam([12, ["ok", [0]]]); +const re6 = reparam(["ok", [12, ["k"]]]); +const re7 = reparam([12, "not ok"]); // arity error +const re8 = reparam(["ok", [12, ["ok", [12, "not ok"]]]]); // deep arity error +const re9 = reparam([12, [12]]); // non-alternating +const re10 = reparam(["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]]); // deep non-alternating - we should strive to issue an error here, I think, but we infer `string | number` for T and do not + + + +//// [selfReferentialTypeAliases.js] +var hypertextNode = ["div", { id: "parent" }, + ["div", { id: "first-child" }, "I'm the first child"], + ["div", { id: "second-child" }, "I'm the second child"] +]; +// inference for this alternating reference pattern is.... interesting. +var re1 = reparam([12]); +var re2 = reparam(["ok"]); +var re3 = reparam([12, ["ok"]]); +var re4 = reparam(["ok", [12]]); +var re5 = reparam([12, ["ok", [0]]]); +var re6 = reparam(["ok", [12, ["k"]]]); +var re7 = reparam([12, "not ok"]); // arity error +var re8 = reparam(["ok", [12, ["ok", [12, "not ok"]]]]); // deep arity error +var re9 = reparam([12, [12]]); // non-alternating +var re10 = reparam(["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]]); // deep non-alternating - we should strive to issue an error here, I think, but we infer `string | number` for T and do not diff --git a/tests/baselines/reference/selfReferentialTypeAliases.symbols b/tests/baselines/reference/selfReferentialTypeAliases.symbols new file mode 100644 index 0000000000000..234a7986a6ba9 --- /dev/null +++ b/tests/baselines/reference/selfReferentialTypeAliases.symbols @@ -0,0 +1,78 @@ +=== tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts === +type HypertextNode = string | [string, { [key: string]: any }, ...HypertextNode[]]; +>HypertextNode : Symbol(HypertextNode, Decl(selfReferentialTypeAliases.ts, 0, 0)) +>key : Symbol(key, Decl(selfReferentialTypeAliases.ts, 0, 42)) +>HypertextNode : Symbol(HypertextNode, Decl(selfReferentialTypeAliases.ts, 0, 0)) + +const hypertextNode: HypertextNode = +>hypertextNode : Symbol(hypertextNode, Decl(selfReferentialTypeAliases.ts, 2, 5)) +>HypertextNode : Symbol(HypertextNode, Decl(selfReferentialTypeAliases.ts, 0, 0)) + + ["div", { id: "parent" }, +>id : Symbol(id, Decl(selfReferentialTypeAliases.ts, 3, 13)) + + ["div", { id: "first-child" }, "I'm the first child"], +>id : Symbol(id, Decl(selfReferentialTypeAliases.ts, 4, 17)) + + ["div", { id: "second-child" }, "I'm the second child"] +>id : Symbol(id, Decl(selfReferentialTypeAliases.ts, 5, 17)) + + ]; + +type Alternating = [T, Alternating?]; +>Alternating : Symbol(Alternating, Decl(selfReferentialTypeAliases.ts, 6, 6)) +>T : Symbol(T, Decl(selfReferentialTypeAliases.ts, 8, 17)) +>T : Symbol(T, Decl(selfReferentialTypeAliases.ts, 8, 17)) +>Alternating : Symbol(Alternating, Decl(selfReferentialTypeAliases.ts, 6, 6)) +>T : Symbol(T, Decl(selfReferentialTypeAliases.ts, 8, 17)) + +declare function reparam(x: Alternating): T; +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) +>T : Symbol(T, Decl(selfReferentialTypeAliases.ts, 10, 25)) +>x : Symbol(x, Decl(selfReferentialTypeAliases.ts, 10, 28)) +>Alternating : Symbol(Alternating, Decl(selfReferentialTypeAliases.ts, 6, 6)) +>T : Symbol(T, Decl(selfReferentialTypeAliases.ts, 10, 25)) +>T : Symbol(T, Decl(selfReferentialTypeAliases.ts, 10, 25)) + +// inference for this alternating reference pattern is.... interesting. +const re1 = reparam([12]); +>re1 : Symbol(re1, Decl(selfReferentialTypeAliases.ts, 13, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re2 = reparam(["ok"]); +>re2 : Symbol(re2, Decl(selfReferentialTypeAliases.ts, 14, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re3 = reparam([12, ["ok"]]); +>re3 : Symbol(re3, Decl(selfReferentialTypeAliases.ts, 15, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re4 = reparam(["ok", [12]]); +>re4 : Symbol(re4, Decl(selfReferentialTypeAliases.ts, 16, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re5 = reparam([12, ["ok", [0]]]); +>re5 : Symbol(re5, Decl(selfReferentialTypeAliases.ts, 17, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re6 = reparam(["ok", [12, ["k"]]]); +>re6 : Symbol(re6, Decl(selfReferentialTypeAliases.ts, 18, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re7 = reparam([12, "not ok"]); // arity error +>re7 : Symbol(re7, Decl(selfReferentialTypeAliases.ts, 19, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re8 = reparam(["ok", [12, ["ok", [12, "not ok"]]]]); // deep arity error +>re8 : Symbol(re8, Decl(selfReferentialTypeAliases.ts, 20, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re9 = reparam([12, [12]]); // non-alternating +>re9 : Symbol(re9, Decl(selfReferentialTypeAliases.ts, 21, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + +const re10 = reparam(["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]]); // deep non-alternating - we should strive to issue an error here, I think, but we infer `string | number` for T and do not +>re10 : Symbol(re10, Decl(selfReferentialTypeAliases.ts, 22, 5)) +>reparam : Symbol(reparam, Decl(selfReferentialTypeAliases.ts, 8, 76)) + + diff --git a/tests/baselines/reference/selfReferentialTypeAliases.types b/tests/baselines/reference/selfReferentialTypeAliases.types new file mode 100644 index 0000000000000..70fa40908d968 --- /dev/null +++ b/tests/baselines/reference/selfReferentialTypeAliases.types @@ -0,0 +1,144 @@ +=== tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts === +type HypertextNode = string | [string, { [key: string]: any }, ...HypertextNode[]]; +>HypertextNode : HypertextNode +>key : string + +const hypertextNode: HypertextNode = +>hypertextNode : HypertextNode + + ["div", { id: "parent" }, +>["div", { id: "parent" }, ["div", { id: "first-child" }, "I'm the first child"], ["div", { id: "second-child" }, "I'm the second child"] ] : [string, { id: string; }, [string, { id: string; }, "I'm the first child"], [string, { id: string; }, "I'm the second child"]] +>"div" : "div" +>{ id: "parent" } : { id: string; } +>id : string +>"parent" : "parent" + + ["div", { id: "first-child" }, "I'm the first child"], +>["div", { id: "first-child" }, "I'm the first child"] : [string, { id: string; }, "I'm the first child"] +>"div" : "div" +>{ id: "first-child" } : { id: string; } +>id : string +>"first-child" : "first-child" +>"I'm the first child" : "I'm the first child" + + ["div", { id: "second-child" }, "I'm the second child"] +>["div", { id: "second-child" }, "I'm the second child"] : [string, { id: string; }, "I'm the second child"] +>"div" : "div" +>{ id: "second-child" } : { id: string; } +>id : string +>"second-child" : "second-child" +>"I'm the second child" : "I'm the second child" + + ]; + +type Alternating = [T, Alternating?]; +>Alternating : [T, Alternating?] + +declare function reparam(x: Alternating): T; +>reparam : (x: [T, Alternating?]) => T +>x : [T, Alternating?] + +// inference for this alternating reference pattern is.... interesting. +const re1 = reparam([12]); +>re1 : number +>reparam([12]) : number +>reparam : (x: [T, Alternating?]) => T +>[12] : [number] +>12 : 12 + +const re2 = reparam(["ok"]); +>re2 : string +>reparam(["ok"]) : string +>reparam : (x: [T, Alternating?]) => T +>["ok"] : [string] +>"ok" : "ok" + +const re3 = reparam([12, ["ok"]]); +>re3 : number | ["ok"] +>reparam([12, ["ok"]]) : number | ["ok"] +>reparam : (x: [T, Alternating?]) => T +>[12, ["ok"]] : [number, [string]] +>12 : 12 +>["ok"] : [string] +>"ok" : "ok" + +const re4 = reparam(["ok", [12]]); +>re4 : string | [12] +>reparam(["ok", [12]]) : string | [12] +>reparam : (x: [T, Alternating?]) => T +>["ok", [12]] : [string, [number]] +>"ok" : "ok" +>[12] : [number] +>12 : 12 + +const re5 = reparam([12, ["ok", [0]]]); +>re5 : number | ["ok", [0]] +>reparam([12, ["ok", [0]]]) : number | ["ok", [0]] +>reparam : (x: [T, Alternating?]) => T +>[12, ["ok", [0]]] : [number, [string, [number]]] +>12 : 12 +>["ok", [0]] : [string, [number]] +>"ok" : "ok" +>[0] : [number] +>0 : 0 + +const re6 = reparam(["ok", [12, ["k"]]]); +>re6 : string | [12, ["k"]] +>reparam(["ok", [12, ["k"]]]) : string | [12, ["k"]] +>reparam : (x: [T, Alternating?]) => T +>["ok", [12, ["k"]]] : [string, [number, [string]]] +>"ok" : "ok" +>[12, ["k"]] : [number, [string]] +>12 : 12 +>["k"] : [string] +>"k" : "k" + +const re7 = reparam([12, "not ok"]); // arity error +>re7 : any +>reparam([12, "not ok"]) : any +>reparam : (x: [T, Alternating?]) => T +>[12, "not ok"] : (string | number)[] +>12 : 12 +>"not ok" : "not ok" + +const re8 = reparam(["ok", [12, ["ok", [12, "not ok"]]]]); // deep arity error +>re8 : any +>reparam(["ok", [12, ["ok", [12, "not ok"]]]]) : any +>reparam : (x: [T, Alternating?]) => T +>["ok", [12, ["ok", [12, "not ok"]]]] : (string | (number | (string | (string | number)[])[])[])[] +>"ok" : "ok" +>[12, ["ok", [12, "not ok"]]] : (number | (string | (string | number)[])[])[] +>12 : 12 +>["ok", [12, "not ok"]] : (string | (string | number)[])[] +>"ok" : "ok" +>[12, "not ok"] : (string | number)[] +>12 : 12 +>"not ok" : "not ok" + +const re9 = reparam([12, [12]]); // non-alternating +>re9 : any +>reparam([12, [12]]) : any +>reparam : (x: [T, Alternating?]) => T +>[12, [12]] : (number | number[])[] +>12 : 12 +>[12] : number[] +>12 : 12 + +const re10 = reparam(["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]]); // deep non-alternating - we should strive to issue an error here, I think, but we infer `string | number` for T and do not +>re10 : string | [12, ["ok", [12, ["ok", ["not ok"]]]]] +>reparam(["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]]) : string | [12, ["ok", [12, ["ok", ["not ok"]]]]] +>reparam : (x: [T, Alternating?]) => T +>["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]] : [string, [number, [string, [number, [string, [string]]]]]] +>"ok" : "ok" +>[12, ["ok", [12, ["ok", ["not ok"]]]]] : [number, [string, [number, [string, [string]]]]] +>12 : 12 +>["ok", [12, ["ok", ["not ok"]]]] : [string, [number, [string, [string]]]] +>"ok" : "ok" +>[12, ["ok", ["not ok"]]] : [number, [string, [string]]] +>12 : 12 +>["ok", ["not ok"]] : [string, [string]] +>"ok" : "ok" +>["not ok"] : [string] +>"not ok" : "not ok" + + diff --git a/tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts b/tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts new file mode 100644 index 0000000000000..1d5ef5c48c067 --- /dev/null +++ b/tests/cases/conformance/types/typeAliases/selfReferentialTypeAliases.ts @@ -0,0 +1,24 @@ +type HypertextNode = string | [string, { [key: string]: any }, ...HypertextNode[]]; + +const hypertextNode: HypertextNode = + ["div", { id: "parent" }, + ["div", { id: "first-child" }, "I'm the first child"], + ["div", { id: "second-child" }, "I'm the second child"] + ]; + +type Alternating = [T, Alternating?]; + +declare function reparam(x: Alternating): T; + +// inference for this alternating reference pattern is.... interesting. +const re1 = reparam([12]); +const re2 = reparam(["ok"]); +const re3 = reparam([12, ["ok"]]); +const re4 = reparam(["ok", [12]]); +const re5 = reparam([12, ["ok", [0]]]); +const re6 = reparam(["ok", [12, ["k"]]]); +const re7 = reparam([12, "not ok"]); // arity error +const re8 = reparam(["ok", [12, ["ok", [12, "not ok"]]]]); // deep arity error +const re9 = reparam([12, [12]]); // non-alternating +const re10 = reparam(["ok", [12, ["ok", [12, ["ok", ["not ok"]]]]]]); // deep non-alternating - we should strive to issue an error here, I think, but we infer `string | number` for T and do not + diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 1e471a007968b..2314640485001 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 1e471a007968b7490563b91ed6909ae6046f3fe8 +Subproject commit 23146404850011972f695fb6bc2b8113c3cffbfc