diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a8a92caf90451..79352c1128043 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12048,13 +12048,24 @@ namespace ts { return type; } + function isMappedTypeGenericIndexedAccess(type: Type) { + return type.flags & TypeFlags.IndexedAccess && getObjectFlags((type as IndexedAccessType).objectType) & ObjectFlags.Mapped && + !isGenericMappedType((type as IndexedAccessType).objectType) && isGenericIndexType((type as IndexedAccessType).indexType); + } + /** * For a type parameter, return the base constraint of the type parameter. For the string, number, * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the * type itself. */ function getApparentType(type: Type): Type { - const t = type.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(type) || unknownType : type; + // We obtain the base constraint for all instantiable types, except indexed access types of the form + // { [P in K]: E }[X], where K is non-generic and X is generic. For those types, we instead substitute an + // instantiation of E where P is replaced with X. We do this because getBaseConstraintOfType directly + // lowers to an instantiation where X's constraint is substituted for X, which isn't always desirable. + const t = !(type.flags & TypeFlags.Instantiable) ? type : + isMappedTypeGenericIndexedAccess(type) ? substituteIndexedMappedType((type as IndexedAccessType).objectType as MappedType, (type as IndexedAccessType).indexType) : + getBaseConstraintOfType(type) || unknownType; return getObjectFlags(t) & ObjectFlags.Mapped ? getApparentTypeOfMappedType(t as MappedType) : t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(t as IntersectionType) : t.flags & TypeFlags.StringLike ? globalStringType : @@ -21845,12 +21856,14 @@ namespace ts { // not contain anyFunctionType when we come back to this argument for its second round // of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard // when constructing types from type parameters that had no inference candidates). - if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === nonInferrableAnyType || source === silentNeverType || - (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType)) || isFromInferenceBlockedSource(source)) { + if (source === nonInferrableAnyType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType)) || isFromInferenceBlockedSource(source)) { return; } const inference = getInferenceInfoForType(target); if (inference) { + if (getObjectFlags(source) & ObjectFlags.NonInferrableType) { + return; + } if (!inference.isFixed) { if (inference.priority === undefined || priority < inference.priority) { inference.candidates = undefined; @@ -21881,21 +21894,19 @@ namespace ts { inferencePriority = Math.min(inferencePriority, priority); return; } - else { - // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine - const simplified = getSimplifiedType(target, /*writing*/ false); - if (simplified !== target) { - invokeOnce(source, simplified, inferFromTypes); - } - else if (target.flags & TypeFlags.IndexedAccess) { - const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false); - // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider - // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can. - if (indexType.flags & TypeFlags.Instantiable) { - const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false); - if (simplified && simplified !== target) { - invokeOnce(source, simplified, inferFromTypes); - } + // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine + const simplified = getSimplifiedType(target, /*writing*/ false); + if (simplified !== target) { + inferFromTypes(source, simplified); + } + else if (target.flags & TypeFlags.IndexedAccess) { + const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false); + // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider + // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can. + if (indexType.flags & TypeFlags.Instantiable) { + const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false); + if (simplified && simplified !== target) { + inferFromTypes(source, simplified); } } } @@ -24929,7 +24940,7 @@ namespace ts { const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) && someType(type, isGenericTypeWithUnionConstraint) && (isConstraintPosition(type, reference) || hasNonBindingPatternContextualTypeWithNoGenericTypes(reference)); - return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOrType(t) : t) : type; + return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable && !isMappedTypeGenericIndexedAccess(t) ? getBaseConstraintOrType(t) : t) : type; } function isExportOrExportExpression(location: Node) { diff --git a/tests/baselines/reference/correlatedUnions.js b/tests/baselines/reference/correlatedUnions.js new file mode 100644 index 0000000000000..7d0d71e9beea2 --- /dev/null +++ b/tests/baselines/reference/correlatedUnions.js @@ -0,0 +1,350 @@ +//// [correlatedUnions.ts] +// Various repros from #30581 + +type RecordMap = { n: number, s: string, b: boolean }; +type UnionRecord = { [P in K]: { + kind: P, + v: RecordMap[P], + f: (v: RecordMap[P]) => void +}}[K]; + +function processRecord(rec: UnionRecord) { + rec.f(rec.v); +} + +declare const r1: UnionRecord<'n'>; // { kind: 'n', v: number, f: (v: number) => void } +declare const r2: UnionRecord; // { kind: 'n', ... } | { kind: 's', ... } | { kind: 'b', ... } + +processRecord(r1); +processRecord(r2); +processRecord({ kind: 'n', v: 42, f: v => v.toExponential() }); + +// -------- + +type TextFieldData = { value: string } +type SelectFieldData = { options: string[], selectedValue: string } + +type FieldMap = { + text: TextFieldData; + select: SelectFieldData; +} + +type FormField = { type: K, data: FieldMap[K] }; + +type RenderFunc = (props: FieldMap[K]) => void; +type RenderFuncMap = { [K in keyof FieldMap]: RenderFunc }; + +function renderTextField(props: TextFieldData) {} +function renderSelectField(props: SelectFieldData) {} + +const renderFuncs: RenderFuncMap = { + text: renderTextField, + select: renderSelectField, +}; + +function renderField(field: FormField) { + const renderFn = renderFuncs[field.type]; + renderFn(field.data); +} + +// -------- + +type TypeMap = { + foo: string, + bar: number +}; + +type Keys = keyof TypeMap; + +type HandlerMap = { [P in Keys]: (x: TypeMap[P]) => void }; + +const handlers: HandlerMap = { + foo: s => s.length, + bar: n => n.toFixed(2) +}; + +type DataEntry = { [P in K]: { + type: P, + data: TypeMap[P] +}}[K]; + +const data: DataEntry[] = [ + { type: 'foo', data: 'abc' }, + { type: 'foo', data: 'def' }, + { type: 'bar', data: 42 }, +]; + +function process(data: DataEntry[]) { + data.forEach(block => { + if (block.type in handlers) { + handlers[block.type](block.data) + } + }); +} + +process(data); +process([{ type: 'foo', data: 'abc' }]); + +// -------- + +type LetterMap = { A: string, B: number } +type LetterCaller = { [P in K]: { letter: Record, caller: (x: Record) => void } }[K]; + +function call({ letter, caller }: LetterCaller): void { + caller(letter); +} + +type A = { A: string }; +type B = { B: number }; +type ACaller = (a: A) => void; +type BCaller = (b: B) => void; + +declare const xx: { letter: A, caller: ACaller } | { letter: B, caller: BCaller }; + +call(xx); + +// -------- + +type Ev = { [P in K]: { + readonly name: P; + readonly once?: boolean; + readonly callback: (ev: DocumentEventMap[P]) => void; +}}[K]; + +function processEvents(events: Ev[]) { + for (const event of events) { + document.addEventListener(event.name, (ev) => event.callback(ev), { once: event.once }); + } +} + +function createEventListener({ name, once = false, callback }: Ev): Ev { + return { name, once, callback }; +} + +const clickEvent = createEventListener({ + name: "click", + callback: ev => console.log(ev), +}); + +const scrollEvent = createEventListener({ + name: "scroll", + callback: ev => console.log(ev), +}); + +processEvents([clickEvent, scrollEvent]); + +processEvents([ + { name: "click", callback: ev => console.log(ev) }, + { name: "scroll", callback: ev => console.log(ev) }, +]); + +// -------- + +function ff1() { + type ArgMap = { + sum: [a: number, b: number], + concat: [a: string, b: string, c: string] + } + type Keys = keyof ArgMap; + const funs: { [P in Keys]: (...args: ArgMap[P]) => void } = { + sum: (a, b) => a + b, + concat: (a, b, c) => a + b + c + } + function apply(funKey: K, ...args: ArgMap[K]) { + const fn = funs[funKey]; + fn(...args); + } + const x1 = apply('sum', 1, 2) + const x2 = apply('concat', 'str1', 'str2', 'str3' ) +} + + +//// [correlatedUnions.js] +"use strict"; +// Various repros from #30581 +function processRecord(rec) { + rec.f(rec.v); +} +processRecord(r1); +processRecord(r2); +processRecord({ kind: 'n', v: 42, f: function (v) { return v.toExponential(); } }); +function renderTextField(props) { } +function renderSelectField(props) { } +var renderFuncs = { + text: renderTextField, + select: renderSelectField +}; +function renderField(field) { + var renderFn = renderFuncs[field.type]; + renderFn(field.data); +} +var handlers = { + foo: function (s) { return s.length; }, + bar: function (n) { return n.toFixed(2); } +}; +var data = [ + { type: 'foo', data: 'abc' }, + { type: 'foo', data: 'def' }, + { type: 'bar', data: 42 }, +]; +function process(data) { + data.forEach(function (block) { + if (block.type in handlers) { + handlers[block.type](block.data); + } + }); +} +process(data); +process([{ type: 'foo', data: 'abc' }]); +function call(_a) { + var letter = _a.letter, caller = _a.caller; + caller(letter); +} +call(xx); +function processEvents(events) { + var _loop_1 = function (event_1) { + document.addEventListener(event_1.name, function (ev) { return event_1.callback(ev); }, { once: event_1.once }); + }; + for (var _i = 0, events_1 = events; _i < events_1.length; _i++) { + var event_1 = events_1[_i]; + _loop_1(event_1); + } +} +function createEventListener(_a) { + var name = _a.name, _b = _a.once, once = _b === void 0 ? false : _b, callback = _a.callback; + return { name: name, once: once, callback: callback }; +} +var clickEvent = createEventListener({ + name: "click", + callback: function (ev) { return console.log(ev); } +}); +var scrollEvent = createEventListener({ + name: "scroll", + callback: function (ev) { return console.log(ev); } +}); +processEvents([clickEvent, scrollEvent]); +processEvents([ + { name: "click", callback: function (ev) { return console.log(ev); } }, + { name: "scroll", callback: function (ev) { return console.log(ev); } }, +]); +// -------- +function ff1() { + var funs = { + sum: function (a, b) { return a + b; }, + concat: function (a, b, c) { return a + b + c; } + }; + function apply(funKey) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + var fn = funs[funKey]; + fn.apply(void 0, args); + } + var x1 = apply('sum', 1, 2); + var x2 = apply('concat', 'str1', 'str2', 'str3'); +} + + +//// [correlatedUnions.d.ts] +declare type RecordMap = { + n: number; + s: string; + b: boolean; +}; +declare type UnionRecord = { + [P in K]: { + kind: P; + v: RecordMap[P]; + f: (v: RecordMap[P]) => void; + }; +}[K]; +declare function processRecord(rec: UnionRecord): void; +declare const r1: UnionRecord<'n'>; +declare const r2: UnionRecord; +declare type TextFieldData = { + value: string; +}; +declare type SelectFieldData = { + options: string[]; + selectedValue: string; +}; +declare type FieldMap = { + text: TextFieldData; + select: SelectFieldData; +}; +declare type FormField = { + type: K; + data: FieldMap[K]; +}; +declare type RenderFunc = (props: FieldMap[K]) => void; +declare type RenderFuncMap = { + [K in keyof FieldMap]: RenderFunc; +}; +declare function renderTextField(props: TextFieldData): void; +declare function renderSelectField(props: SelectFieldData): void; +declare const renderFuncs: RenderFuncMap; +declare function renderField(field: FormField): void; +declare type TypeMap = { + foo: string; + bar: number; +}; +declare type Keys = keyof TypeMap; +declare type HandlerMap = { + [P in Keys]: (x: TypeMap[P]) => void; +}; +declare const handlers: HandlerMap; +declare type DataEntry = { + [P in K]: { + type: P; + data: TypeMap[P]; + }; +}[K]; +declare const data: DataEntry[]; +declare function process(data: DataEntry[]): void; +declare type LetterMap = { + A: string; + B: number; +}; +declare type LetterCaller = { + [P in K]: { + letter: Record; + caller: (x: Record) => void; + }; +}[K]; +declare function call({ letter, caller }: LetterCaller): void; +declare type A = { + A: string; +}; +declare type B = { + B: number; +}; +declare type ACaller = (a: A) => void; +declare type BCaller = (b: B) => void; +declare const xx: { + letter: A; + caller: ACaller; +} | { + letter: B; + caller: BCaller; +}; +declare type Ev = { + [P in K]: { + readonly name: P; + readonly once?: boolean; + readonly callback: (ev: DocumentEventMap[P]) => void; + }; +}[K]; +declare function processEvents(events: Ev[]): void; +declare function createEventListener({ name, once, callback }: Ev): Ev; +declare const clickEvent: { + readonly name: "click"; + readonly once?: boolean | undefined; + readonly callback: (ev: MouseEvent) => void; +}; +declare const scrollEvent: { + readonly name: "scroll"; + readonly once?: boolean | undefined; + readonly callback: (ev: Event) => void; +}; +declare function ff1(): void; diff --git a/tests/baselines/reference/correlatedUnions.symbols b/tests/baselines/reference/correlatedUnions.symbols new file mode 100644 index 0000000000000..ecb57667c815c --- /dev/null +++ b/tests/baselines/reference/correlatedUnions.symbols @@ -0,0 +1,575 @@ +=== tests/cases/compiler/correlatedUnions.ts === +// Various repros from #30581 + +type RecordMap = { n: number, s: string, b: boolean }; +>RecordMap : Symbol(RecordMap, Decl(correlatedUnions.ts, 0, 0)) +>n : Symbol(n, Decl(correlatedUnions.ts, 2, 18)) +>s : Symbol(s, Decl(correlatedUnions.ts, 2, 29)) +>b : Symbol(b, Decl(correlatedUnions.ts, 2, 40)) + +type UnionRecord = { [P in K]: { +>UnionRecord : Symbol(UnionRecord, Decl(correlatedUnions.ts, 2, 54)) +>K : Symbol(K, Decl(correlatedUnions.ts, 3, 17)) +>RecordMap : Symbol(RecordMap, Decl(correlatedUnions.ts, 0, 0)) +>RecordMap : Symbol(RecordMap, Decl(correlatedUnions.ts, 0, 0)) +>P : Symbol(P, Decl(correlatedUnions.ts, 3, 67)) +>K : Symbol(K, Decl(correlatedUnions.ts, 3, 17)) + + kind: P, +>kind : Symbol(kind, Decl(correlatedUnions.ts, 3, 77)) +>P : Symbol(P, Decl(correlatedUnions.ts, 3, 67)) + + v: RecordMap[P], +>v : Symbol(v, Decl(correlatedUnions.ts, 4, 12)) +>RecordMap : Symbol(RecordMap, Decl(correlatedUnions.ts, 0, 0)) +>P : Symbol(P, Decl(correlatedUnions.ts, 3, 67)) + + f: (v: RecordMap[P]) => void +>f : Symbol(f, Decl(correlatedUnions.ts, 5, 20)) +>v : Symbol(v, Decl(correlatedUnions.ts, 6, 8)) +>RecordMap : Symbol(RecordMap, Decl(correlatedUnions.ts, 0, 0)) +>P : Symbol(P, Decl(correlatedUnions.ts, 3, 67)) + +}}[K]; +>K : Symbol(K, Decl(correlatedUnions.ts, 3, 17)) + +function processRecord(rec: UnionRecord) { +>processRecord : Symbol(processRecord, Decl(correlatedUnions.ts, 7, 6)) +>K : Symbol(K, Decl(correlatedUnions.ts, 9, 23)) +>RecordMap : Symbol(RecordMap, Decl(correlatedUnions.ts, 0, 0)) +>rec : Symbol(rec, Decl(correlatedUnions.ts, 9, 50)) +>UnionRecord : Symbol(UnionRecord, Decl(correlatedUnions.ts, 2, 54)) +>K : Symbol(K, Decl(correlatedUnions.ts, 9, 23)) + + rec.f(rec.v); +>rec.f : Symbol(f, Decl(correlatedUnions.ts, 5, 20)) +>rec : Symbol(rec, Decl(correlatedUnions.ts, 9, 50)) +>f : Symbol(f, Decl(correlatedUnions.ts, 5, 20)) +>rec.v : Symbol(v, Decl(correlatedUnions.ts, 4, 12)) +>rec : Symbol(rec, Decl(correlatedUnions.ts, 9, 50)) +>v : Symbol(v, Decl(correlatedUnions.ts, 4, 12)) +} + +declare const r1: UnionRecord<'n'>; // { kind: 'n', v: number, f: (v: number) => void } +>r1 : Symbol(r1, Decl(correlatedUnions.ts, 13, 13)) +>UnionRecord : Symbol(UnionRecord, Decl(correlatedUnions.ts, 2, 54)) + +declare const r2: UnionRecord; // { kind: 'n', ... } | { kind: 's', ... } | { kind: 'b', ... } +>r2 : Symbol(r2, Decl(correlatedUnions.ts, 14, 13)) +>UnionRecord : Symbol(UnionRecord, Decl(correlatedUnions.ts, 2, 54)) + +processRecord(r1); +>processRecord : Symbol(processRecord, Decl(correlatedUnions.ts, 7, 6)) +>r1 : Symbol(r1, Decl(correlatedUnions.ts, 13, 13)) + +processRecord(r2); +>processRecord : Symbol(processRecord, Decl(correlatedUnions.ts, 7, 6)) +>r2 : Symbol(r2, Decl(correlatedUnions.ts, 14, 13)) + +processRecord({ kind: 'n', v: 42, f: v => v.toExponential() }); +>processRecord : Symbol(processRecord, Decl(correlatedUnions.ts, 7, 6)) +>kind : Symbol(kind, Decl(correlatedUnions.ts, 18, 15)) +>v : Symbol(v, Decl(correlatedUnions.ts, 18, 26)) +>f : Symbol(f, Decl(correlatedUnions.ts, 18, 33)) +>v : Symbol(v, Decl(correlatedUnions.ts, 18, 36)) +>v.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>v : Symbol(v, Decl(correlatedUnions.ts, 18, 36)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + +// -------- + +type TextFieldData = { value: string } +>TextFieldData : Symbol(TextFieldData, Decl(correlatedUnions.ts, 18, 63)) +>value : Symbol(value, Decl(correlatedUnions.ts, 22, 22)) + +type SelectFieldData = { options: string[], selectedValue: string } +>SelectFieldData : Symbol(SelectFieldData, Decl(correlatedUnions.ts, 22, 38)) +>options : Symbol(options, Decl(correlatedUnions.ts, 23, 24)) +>selectedValue : Symbol(selectedValue, Decl(correlatedUnions.ts, 23, 43)) + +type FieldMap = { +>FieldMap : Symbol(FieldMap, Decl(correlatedUnions.ts, 23, 67)) + + text: TextFieldData; +>text : Symbol(text, Decl(correlatedUnions.ts, 25, 17)) +>TextFieldData : Symbol(TextFieldData, Decl(correlatedUnions.ts, 18, 63)) + + select: SelectFieldData; +>select : Symbol(select, Decl(correlatedUnions.ts, 26, 24)) +>SelectFieldData : Symbol(SelectFieldData, Decl(correlatedUnions.ts, 22, 38)) +} + +type FormField = { type: K, data: FieldMap[K] }; +>FormField : Symbol(FormField, Decl(correlatedUnions.ts, 28, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 30, 15)) +>FieldMap : Symbol(FieldMap, Decl(correlatedUnions.ts, 23, 67)) +>type : Symbol(type, Decl(correlatedUnions.ts, 30, 44)) +>K : Symbol(K, Decl(correlatedUnions.ts, 30, 15)) +>data : Symbol(data, Decl(correlatedUnions.ts, 30, 53)) +>FieldMap : Symbol(FieldMap, Decl(correlatedUnions.ts, 23, 67)) +>K : Symbol(K, Decl(correlatedUnions.ts, 30, 15)) + +type RenderFunc = (props: FieldMap[K]) => void; +>RenderFunc : Symbol(RenderFunc, Decl(correlatedUnions.ts, 30, 74)) +>K : Symbol(K, Decl(correlatedUnions.ts, 32, 16)) +>FieldMap : Symbol(FieldMap, Decl(correlatedUnions.ts, 23, 67)) +>props : Symbol(props, Decl(correlatedUnions.ts, 32, 45)) +>FieldMap : Symbol(FieldMap, Decl(correlatedUnions.ts, 23, 67)) +>K : Symbol(K, Decl(correlatedUnions.ts, 32, 16)) + +type RenderFuncMap = { [K in keyof FieldMap]: RenderFunc }; +>RenderFuncMap : Symbol(RenderFuncMap, Decl(correlatedUnions.ts, 32, 73)) +>K : Symbol(K, Decl(correlatedUnions.ts, 33, 24)) +>FieldMap : Symbol(FieldMap, Decl(correlatedUnions.ts, 23, 67)) +>RenderFunc : Symbol(RenderFunc, Decl(correlatedUnions.ts, 30, 74)) +>K : Symbol(K, Decl(correlatedUnions.ts, 33, 24)) + +function renderTextField(props: TextFieldData) {} +>renderTextField : Symbol(renderTextField, Decl(correlatedUnions.ts, 33, 62)) +>props : Symbol(props, Decl(correlatedUnions.ts, 35, 25)) +>TextFieldData : Symbol(TextFieldData, Decl(correlatedUnions.ts, 18, 63)) + +function renderSelectField(props: SelectFieldData) {} +>renderSelectField : Symbol(renderSelectField, Decl(correlatedUnions.ts, 35, 49)) +>props : Symbol(props, Decl(correlatedUnions.ts, 36, 27)) +>SelectFieldData : Symbol(SelectFieldData, Decl(correlatedUnions.ts, 22, 38)) + +const renderFuncs: RenderFuncMap = { +>renderFuncs : Symbol(renderFuncs, Decl(correlatedUnions.ts, 38, 5)) +>RenderFuncMap : Symbol(RenderFuncMap, Decl(correlatedUnions.ts, 32, 73)) + + text: renderTextField, +>text : Symbol(text, Decl(correlatedUnions.ts, 38, 36)) +>renderTextField : Symbol(renderTextField, Decl(correlatedUnions.ts, 33, 62)) + + select: renderSelectField, +>select : Symbol(select, Decl(correlatedUnions.ts, 39, 26)) +>renderSelectField : Symbol(renderSelectField, Decl(correlatedUnions.ts, 35, 49)) + +}; + +function renderField(field: FormField) { +>renderField : Symbol(renderField, Decl(correlatedUnions.ts, 41, 2)) +>K : Symbol(K, Decl(correlatedUnions.ts, 43, 21)) +>FieldMap : Symbol(FieldMap, Decl(correlatedUnions.ts, 23, 67)) +>field : Symbol(field, Decl(correlatedUnions.ts, 43, 47)) +>FormField : Symbol(FormField, Decl(correlatedUnions.ts, 28, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 43, 21)) + + const renderFn = renderFuncs[field.type]; +>renderFn : Symbol(renderFn, Decl(correlatedUnions.ts, 44, 9)) +>renderFuncs : Symbol(renderFuncs, Decl(correlatedUnions.ts, 38, 5)) +>field.type : Symbol(type, Decl(correlatedUnions.ts, 30, 44)) +>field : Symbol(field, Decl(correlatedUnions.ts, 43, 47)) +>type : Symbol(type, Decl(correlatedUnions.ts, 30, 44)) + + renderFn(field.data); +>renderFn : Symbol(renderFn, Decl(correlatedUnions.ts, 44, 9)) +>field.data : Symbol(data, Decl(correlatedUnions.ts, 30, 53)) +>field : Symbol(field, Decl(correlatedUnions.ts, 43, 47)) +>data : Symbol(data, Decl(correlatedUnions.ts, 30, 53)) +} + +// -------- + +type TypeMap = { +>TypeMap : Symbol(TypeMap, Decl(correlatedUnions.ts, 46, 1)) + + foo: string, +>foo : Symbol(foo, Decl(correlatedUnions.ts, 50, 16)) + + bar: number +>bar : Symbol(bar, Decl(correlatedUnions.ts, 51, 16)) + +}; + +type Keys = keyof TypeMap; +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 53, 2)) +>TypeMap : Symbol(TypeMap, Decl(correlatedUnions.ts, 46, 1)) + +type HandlerMap = { [P in Keys]: (x: TypeMap[P]) => void }; +>HandlerMap : Symbol(HandlerMap, Decl(correlatedUnions.ts, 55, 26)) +>P : Symbol(P, Decl(correlatedUnions.ts, 57, 21)) +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 53, 2)) +>x : Symbol(x, Decl(correlatedUnions.ts, 57, 34)) +>TypeMap : Symbol(TypeMap, Decl(correlatedUnions.ts, 46, 1)) +>P : Symbol(P, Decl(correlatedUnions.ts, 57, 21)) + +const handlers: HandlerMap = { +>handlers : Symbol(handlers, Decl(correlatedUnions.ts, 59, 5)) +>HandlerMap : Symbol(HandlerMap, Decl(correlatedUnions.ts, 55, 26)) + + foo: s => s.length, +>foo : Symbol(foo, Decl(correlatedUnions.ts, 59, 30)) +>s : Symbol(s, Decl(correlatedUnions.ts, 60, 8)) +>s.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>s : Symbol(s, Decl(correlatedUnions.ts, 60, 8)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + + bar: n => n.toFixed(2) +>bar : Symbol(bar, Decl(correlatedUnions.ts, 60, 23)) +>n : Symbol(n, Decl(correlatedUnions.ts, 61, 8)) +>n.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>n : Symbol(n, Decl(correlatedUnions.ts, 61, 8)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + +}; + +type DataEntry = { [P in K]: { +>DataEntry : Symbol(DataEntry, Decl(correlatedUnions.ts, 62, 2)) +>K : Symbol(K, Decl(correlatedUnions.ts, 64, 15)) +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 53, 2)) +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 53, 2)) +>P : Symbol(P, Decl(correlatedUnions.ts, 64, 43)) +>K : Symbol(K, Decl(correlatedUnions.ts, 64, 15)) + + type: P, +>type : Symbol(type, Decl(correlatedUnions.ts, 64, 53)) +>P : Symbol(P, Decl(correlatedUnions.ts, 64, 43)) + + data: TypeMap[P] +>data : Symbol(data, Decl(correlatedUnions.ts, 65, 12)) +>TypeMap : Symbol(TypeMap, Decl(correlatedUnions.ts, 46, 1)) +>P : Symbol(P, Decl(correlatedUnions.ts, 64, 43)) + +}}[K]; +>K : Symbol(K, Decl(correlatedUnions.ts, 64, 15)) + +const data: DataEntry[] = [ +>data : Symbol(data, Decl(correlatedUnions.ts, 69, 5)) +>DataEntry : Symbol(DataEntry, Decl(correlatedUnions.ts, 62, 2)) + + { type: 'foo', data: 'abc' }, +>type : Symbol(type, Decl(correlatedUnions.ts, 70, 5)) +>data : Symbol(data, Decl(correlatedUnions.ts, 70, 18)) + + { type: 'foo', data: 'def' }, +>type : Symbol(type, Decl(correlatedUnions.ts, 71, 5)) +>data : Symbol(data, Decl(correlatedUnions.ts, 71, 18)) + + { type: 'bar', data: 42 }, +>type : Symbol(type, Decl(correlatedUnions.ts, 72, 5)) +>data : Symbol(data, Decl(correlatedUnions.ts, 72, 18)) + +]; + +function process(data: DataEntry[]) { +>process : Symbol(process, Decl(correlatedUnions.ts, 73, 2)) +>K : Symbol(K, Decl(correlatedUnions.ts, 75, 17)) +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 53, 2)) +>data : Symbol(data, Decl(correlatedUnions.ts, 75, 33)) +>DataEntry : Symbol(DataEntry, Decl(correlatedUnions.ts, 62, 2)) +>K : Symbol(K, Decl(correlatedUnions.ts, 75, 17)) + + data.forEach(block => { +>data.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>data : Symbol(data, Decl(correlatedUnions.ts, 75, 33)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>block : Symbol(block, Decl(correlatedUnions.ts, 76, 17)) + + if (block.type in handlers) { +>block.type : Symbol(type, Decl(correlatedUnions.ts, 64, 53)) +>block : Symbol(block, Decl(correlatedUnions.ts, 76, 17)) +>type : Symbol(type, Decl(correlatedUnions.ts, 64, 53)) +>handlers : Symbol(handlers, Decl(correlatedUnions.ts, 59, 5)) + + handlers[block.type](block.data) +>handlers : Symbol(handlers, Decl(correlatedUnions.ts, 59, 5)) +>block.type : Symbol(type, Decl(correlatedUnions.ts, 64, 53)) +>block : Symbol(block, Decl(correlatedUnions.ts, 76, 17)) +>type : Symbol(type, Decl(correlatedUnions.ts, 64, 53)) +>block.data : Symbol(data, Decl(correlatedUnions.ts, 65, 12)) +>block : Symbol(block, Decl(correlatedUnions.ts, 76, 17)) +>data : Symbol(data, Decl(correlatedUnions.ts, 65, 12)) + } + }); +} + +process(data); +>process : Symbol(process, Decl(correlatedUnions.ts, 73, 2)) +>data : Symbol(data, Decl(correlatedUnions.ts, 69, 5)) + +process([{ type: 'foo', data: 'abc' }]); +>process : Symbol(process, Decl(correlatedUnions.ts, 73, 2)) +>type : Symbol(type, Decl(correlatedUnions.ts, 84, 10)) +>data : Symbol(data, Decl(correlatedUnions.ts, 84, 23)) + +// -------- + +type LetterMap = { A: string, B: number } +>LetterMap : Symbol(LetterMap, Decl(correlatedUnions.ts, 84, 40)) +>A : Symbol(A, Decl(correlatedUnions.ts, 88, 18)) +>B : Symbol(B, Decl(correlatedUnions.ts, 88, 29)) + +type LetterCaller = { [P in K]: { letter: Record, caller: (x: Record) => void } }[K]; +>LetterCaller : Symbol(LetterCaller, Decl(correlatedUnions.ts, 88, 41)) +>K : Symbol(K, Decl(correlatedUnions.ts, 89, 18)) +>LetterMap : Symbol(LetterMap, Decl(correlatedUnions.ts, 84, 40)) +>P : Symbol(P, Decl(correlatedUnions.ts, 89, 50)) +>K : Symbol(K, Decl(correlatedUnions.ts, 89, 18)) +>letter : Symbol(letter, Decl(correlatedUnions.ts, 89, 60)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>P : Symbol(P, Decl(correlatedUnions.ts, 89, 50)) +>LetterMap : Symbol(LetterMap, Decl(correlatedUnions.ts, 84, 40)) +>P : Symbol(P, Decl(correlatedUnions.ts, 89, 50)) +>caller : Symbol(caller, Decl(correlatedUnions.ts, 89, 93)) +>x : Symbol(x, Decl(correlatedUnions.ts, 89, 103)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>P : Symbol(P, Decl(correlatedUnions.ts, 89, 50)) +>LetterMap : Symbol(LetterMap, Decl(correlatedUnions.ts, 84, 40)) +>P : Symbol(P, Decl(correlatedUnions.ts, 89, 50)) +>K : Symbol(K, Decl(correlatedUnions.ts, 89, 18)) + +function call({ letter, caller }: LetterCaller): void { +>call : Symbol(call, Decl(correlatedUnions.ts, 89, 146)) +>K : Symbol(K, Decl(correlatedUnions.ts, 91, 14)) +>LetterMap : Symbol(LetterMap, Decl(correlatedUnions.ts, 84, 40)) +>letter : Symbol(letter, Decl(correlatedUnions.ts, 91, 42)) +>caller : Symbol(caller, Decl(correlatedUnions.ts, 91, 50)) +>LetterCaller : Symbol(LetterCaller, Decl(correlatedUnions.ts, 88, 41)) +>K : Symbol(K, Decl(correlatedUnions.ts, 91, 14)) + + caller(letter); +>caller : Symbol(caller, Decl(correlatedUnions.ts, 91, 50)) +>letter : Symbol(letter, Decl(correlatedUnions.ts, 91, 42)) +} + +type A = { A: string }; +>A : Symbol(A, Decl(correlatedUnions.ts, 93, 1)) +>A : Symbol(A, Decl(correlatedUnions.ts, 95, 10)) + +type B = { B: number }; +>B : Symbol(B, Decl(correlatedUnions.ts, 95, 23)) +>B : Symbol(B, Decl(correlatedUnions.ts, 96, 10)) + +type ACaller = (a: A) => void; +>ACaller : Symbol(ACaller, Decl(correlatedUnions.ts, 96, 23)) +>a : Symbol(a, Decl(correlatedUnions.ts, 97, 16)) +>A : Symbol(A, Decl(correlatedUnions.ts, 93, 1)) + +type BCaller = (b: B) => void; +>BCaller : Symbol(BCaller, Decl(correlatedUnions.ts, 97, 30)) +>b : Symbol(b, Decl(correlatedUnions.ts, 98, 16)) +>B : Symbol(B, Decl(correlatedUnions.ts, 95, 23)) + +declare const xx: { letter: A, caller: ACaller } | { letter: B, caller: BCaller }; +>xx : Symbol(xx, Decl(correlatedUnions.ts, 100, 13)) +>letter : Symbol(letter, Decl(correlatedUnions.ts, 100, 19)) +>A : Symbol(A, Decl(correlatedUnions.ts, 93, 1)) +>caller : Symbol(caller, Decl(correlatedUnions.ts, 100, 30)) +>ACaller : Symbol(ACaller, Decl(correlatedUnions.ts, 96, 23)) +>letter : Symbol(letter, Decl(correlatedUnions.ts, 100, 52)) +>B : Symbol(B, Decl(correlatedUnions.ts, 95, 23)) +>caller : Symbol(caller, Decl(correlatedUnions.ts, 100, 63)) +>BCaller : Symbol(BCaller, Decl(correlatedUnions.ts, 97, 30)) + +call(xx); +>call : Symbol(call, Decl(correlatedUnions.ts, 89, 146)) +>xx : Symbol(xx, Decl(correlatedUnions.ts, 100, 13)) + +// -------- + +type Ev = { [P in K]: { +>Ev : Symbol(Ev, Decl(correlatedUnions.ts, 102, 9)) +>K : Symbol(K, Decl(correlatedUnions.ts, 106, 8)) +>DocumentEventMap : Symbol(DocumentEventMap, Decl(lib.dom.d.ts, --, --)) +>P : Symbol(P, Decl(correlatedUnions.ts, 106, 47)) +>K : Symbol(K, Decl(correlatedUnions.ts, 106, 8)) + + readonly name: P; +>name : Symbol(name, Decl(correlatedUnions.ts, 106, 57)) +>P : Symbol(P, Decl(correlatedUnions.ts, 106, 47)) + + readonly once?: boolean; +>once : Symbol(once, Decl(correlatedUnions.ts, 107, 21)) + + readonly callback: (ev: DocumentEventMap[P]) => void; +>callback : Symbol(callback, Decl(correlatedUnions.ts, 108, 28)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 109, 24)) +>DocumentEventMap : Symbol(DocumentEventMap, Decl(lib.dom.d.ts, --, --)) +>P : Symbol(P, Decl(correlatedUnions.ts, 106, 47)) + +}}[K]; +>K : Symbol(K, Decl(correlatedUnions.ts, 106, 8)) + +function processEvents(events: Ev[]) { +>processEvents : Symbol(processEvents, Decl(correlatedUnions.ts, 110, 6)) +>K : Symbol(K, Decl(correlatedUnions.ts, 112, 23)) +>DocumentEventMap : Symbol(DocumentEventMap, Decl(lib.dom.d.ts, --, --)) +>events : Symbol(events, Decl(correlatedUnions.ts, 112, 57)) +>Ev : Symbol(Ev, Decl(correlatedUnions.ts, 102, 9)) +>K : Symbol(K, Decl(correlatedUnions.ts, 112, 23)) + + for (const event of events) { +>event : Symbol(event, Decl(correlatedUnions.ts, 113, 14)) +>events : Symbol(events, Decl(correlatedUnions.ts, 112, 57)) + + document.addEventListener(event.name, (ev) => event.callback(ev), { once: event.once }); +>document.addEventListener : Symbol(Document.addEventListener, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>document : Symbol(document, Decl(lib.dom.d.ts, --, --)) +>addEventListener : Symbol(Document.addEventListener, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>event.name : Symbol(name, Decl(correlatedUnions.ts, 106, 57)) +>event : Symbol(event, Decl(correlatedUnions.ts, 113, 14)) +>name : Symbol(name, Decl(correlatedUnions.ts, 106, 57)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 114, 47)) +>event.callback : Symbol(callback, Decl(correlatedUnions.ts, 108, 28)) +>event : Symbol(event, Decl(correlatedUnions.ts, 113, 14)) +>callback : Symbol(callback, Decl(correlatedUnions.ts, 108, 28)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 114, 47)) +>once : Symbol(once, Decl(correlatedUnions.ts, 114, 75)) +>event.once : Symbol(once, Decl(correlatedUnions.ts, 107, 21)) +>event : Symbol(event, Decl(correlatedUnions.ts, 113, 14)) +>once : Symbol(once, Decl(correlatedUnions.ts, 107, 21)) + } +} + +function createEventListener({ name, once = false, callback }: Ev): Ev { +>createEventListener : Symbol(createEventListener, Decl(correlatedUnions.ts, 116, 1)) +>K : Symbol(K, Decl(correlatedUnions.ts, 118, 29)) +>DocumentEventMap : Symbol(DocumentEventMap, Decl(lib.dom.d.ts, --, --)) +>name : Symbol(name, Decl(correlatedUnions.ts, 118, 64)) +>once : Symbol(once, Decl(correlatedUnions.ts, 118, 70)) +>callback : Symbol(callback, Decl(correlatedUnions.ts, 118, 84)) +>Ev : Symbol(Ev, Decl(correlatedUnions.ts, 102, 9)) +>K : Symbol(K, Decl(correlatedUnions.ts, 118, 29)) +>Ev : Symbol(Ev, Decl(correlatedUnions.ts, 102, 9)) +>K : Symbol(K, Decl(correlatedUnions.ts, 118, 29)) + + return { name, once, callback }; +>name : Symbol(name, Decl(correlatedUnions.ts, 119, 12)) +>once : Symbol(once, Decl(correlatedUnions.ts, 119, 18)) +>callback : Symbol(callback, Decl(correlatedUnions.ts, 119, 24)) +} + +const clickEvent = createEventListener({ +>clickEvent : Symbol(clickEvent, Decl(correlatedUnions.ts, 122, 5)) +>createEventListener : Symbol(createEventListener, Decl(correlatedUnions.ts, 116, 1)) + + name: "click", +>name : Symbol(name, Decl(correlatedUnions.ts, 122, 40)) + + callback: ev => console.log(ev), +>callback : Symbol(callback, Decl(correlatedUnions.ts, 123, 18)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 124, 13)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 124, 13)) + +}); + +const scrollEvent = createEventListener({ +>scrollEvent : Symbol(scrollEvent, Decl(correlatedUnions.ts, 127, 5)) +>createEventListener : Symbol(createEventListener, Decl(correlatedUnions.ts, 116, 1)) + + name: "scroll", +>name : Symbol(name, Decl(correlatedUnions.ts, 127, 41)) + + callback: ev => console.log(ev), +>callback : Symbol(callback, Decl(correlatedUnions.ts, 128, 19)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 129, 13)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 129, 13)) + +}); + +processEvents([clickEvent, scrollEvent]); +>processEvents : Symbol(processEvents, Decl(correlatedUnions.ts, 110, 6)) +>clickEvent : Symbol(clickEvent, Decl(correlatedUnions.ts, 122, 5)) +>scrollEvent : Symbol(scrollEvent, Decl(correlatedUnions.ts, 127, 5)) + +processEvents([ +>processEvents : Symbol(processEvents, Decl(correlatedUnions.ts, 110, 6)) + + { name: "click", callback: ev => console.log(ev) }, +>name : Symbol(name, Decl(correlatedUnions.ts, 135, 5)) +>callback : Symbol(callback, Decl(correlatedUnions.ts, 135, 20)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 135, 30)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 135, 30)) + + { name: "scroll", callback: ev => console.log(ev) }, +>name : Symbol(name, Decl(correlatedUnions.ts, 136, 5)) +>callback : Symbol(callback, Decl(correlatedUnions.ts, 136, 21)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 136, 31)) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>ev : Symbol(ev, Decl(correlatedUnions.ts, 136, 31)) + +]); + +// -------- + +function ff1() { +>ff1 : Symbol(ff1, Decl(correlatedUnions.ts, 137, 3)) + + type ArgMap = { +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 141, 16)) + + sum: [a: number, b: number], +>sum : Symbol(sum, Decl(correlatedUnions.ts, 142, 19)) + + concat: [a: string, b: string, c: string] +>concat : Symbol(concat, Decl(correlatedUnions.ts, 143, 36)) + } + type Keys = keyof ArgMap; +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 145, 5)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 141, 16)) + + const funs: { [P in Keys]: (...args: ArgMap[P]) => void } = { +>funs : Symbol(funs, Decl(correlatedUnions.ts, 147, 9)) +>P : Symbol(P, Decl(correlatedUnions.ts, 147, 19)) +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 145, 5)) +>args : Symbol(args, Decl(correlatedUnions.ts, 147, 32)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 141, 16)) +>P : Symbol(P, Decl(correlatedUnions.ts, 147, 19)) + + sum: (a, b) => a + b, +>sum : Symbol(sum, Decl(correlatedUnions.ts, 147, 65)) +>a : Symbol(a, Decl(correlatedUnions.ts, 148, 14)) +>b : Symbol(b, Decl(correlatedUnions.ts, 148, 16)) +>a : Symbol(a, Decl(correlatedUnions.ts, 148, 14)) +>b : Symbol(b, Decl(correlatedUnions.ts, 148, 16)) + + concat: (a, b, c) => a + b + c +>concat : Symbol(concat, Decl(correlatedUnions.ts, 148, 29)) +>a : Symbol(a, Decl(correlatedUnions.ts, 149, 17)) +>b : Symbol(b, Decl(correlatedUnions.ts, 149, 19)) +>c : Symbol(c, Decl(correlatedUnions.ts, 149, 22)) +>a : Symbol(a, Decl(correlatedUnions.ts, 149, 17)) +>b : Symbol(b, Decl(correlatedUnions.ts, 149, 19)) +>c : Symbol(c, Decl(correlatedUnions.ts, 149, 22)) + } + function apply(funKey: K, ...args: ArgMap[K]) { +>apply : Symbol(apply, Decl(correlatedUnions.ts, 150, 5)) +>K : Symbol(K, Decl(correlatedUnions.ts, 151, 19)) +>Keys : Symbol(Keys, Decl(correlatedUnions.ts, 145, 5)) +>funKey : Symbol(funKey, Decl(correlatedUnions.ts, 151, 35)) +>K : Symbol(K, Decl(correlatedUnions.ts, 151, 19)) +>args : Symbol(args, Decl(correlatedUnions.ts, 151, 45)) +>ArgMap : Symbol(ArgMap, Decl(correlatedUnions.ts, 141, 16)) +>K : Symbol(K, Decl(correlatedUnions.ts, 151, 19)) + + const fn = funs[funKey]; +>fn : Symbol(fn, Decl(correlatedUnions.ts, 152, 13)) +>funs : Symbol(funs, Decl(correlatedUnions.ts, 147, 9)) +>funKey : Symbol(funKey, Decl(correlatedUnions.ts, 151, 35)) + + fn(...args); +>fn : Symbol(fn, Decl(correlatedUnions.ts, 152, 13)) +>args : Symbol(args, Decl(correlatedUnions.ts, 151, 45)) + } + const x1 = apply('sum', 1, 2) +>x1 : Symbol(x1, Decl(correlatedUnions.ts, 155, 9)) +>apply : Symbol(apply, Decl(correlatedUnions.ts, 150, 5)) + + const x2 = apply('concat', 'str1', 'str2', 'str3' ) +>x2 : Symbol(x2, Decl(correlatedUnions.ts, 156, 9)) +>apply : Symbol(apply, Decl(correlatedUnions.ts, 150, 5)) +} + diff --git a/tests/baselines/reference/correlatedUnions.types b/tests/baselines/reference/correlatedUnions.types new file mode 100644 index 0000000000000..e10785790a2eb --- /dev/null +++ b/tests/baselines/reference/correlatedUnions.types @@ -0,0 +1,551 @@ +=== tests/cases/compiler/correlatedUnions.ts === +// Various repros from #30581 + +type RecordMap = { n: number, s: string, b: boolean }; +>RecordMap : RecordMap +>n : number +>s : string +>b : boolean + +type UnionRecord = { [P in K]: { +>UnionRecord : UnionRecord + + kind: P, +>kind : P + + v: RecordMap[P], +>v : RecordMap[P] + + f: (v: RecordMap[P]) => void +>f : (v: RecordMap[P]) => void +>v : RecordMap[P] + +}}[K]; + +function processRecord(rec: UnionRecord) { +>processRecord : (rec: UnionRecord) => void +>rec : UnionRecord + + rec.f(rec.v); +>rec.f(rec.v) : void +>rec.f : (v: RecordMap[K]) => void +>rec : UnionRecord +>f : (v: RecordMap[K]) => void +>rec.v : RecordMap[K] +>rec : UnionRecord +>v : RecordMap[K] +} + +declare const r1: UnionRecord<'n'>; // { kind: 'n', v: number, f: (v: number) => void } +>r1 : { kind: "n"; v: number; f: (v: number) => void; } + +declare const r2: UnionRecord; // { kind: 'n', ... } | { kind: 's', ... } | { kind: 'b', ... } +>r2 : UnionRecord + +processRecord(r1); +>processRecord(r1) : void +>processRecord : (rec: UnionRecord) => void +>r1 : { kind: "n"; v: number; f: (v: number) => void; } + +processRecord(r2); +>processRecord(r2) : void +>processRecord : (rec: UnionRecord) => void +>r2 : UnionRecord + +processRecord({ kind: 'n', v: 42, f: v => v.toExponential() }); +>processRecord({ kind: 'n', v: 42, f: v => v.toExponential() }) : void +>processRecord : (rec: UnionRecord) => void +>{ kind: 'n', v: 42, f: v => v.toExponential() } : { kind: "n"; v: number; f: (v: number) => string; } +>kind : "n" +>'n' : "n" +>v : number +>42 : 42 +>f : (v: number) => string +>v => v.toExponential() : (v: number) => string +>v : number +>v.toExponential() : string +>v.toExponential : (fractionDigits?: number | undefined) => string +>v : number +>toExponential : (fractionDigits?: number | undefined) => string + +// -------- + +type TextFieldData = { value: string } +>TextFieldData : TextFieldData +>value : string + +type SelectFieldData = { options: string[], selectedValue: string } +>SelectFieldData : SelectFieldData +>options : string[] +>selectedValue : string + +type FieldMap = { +>FieldMap : FieldMap + + text: TextFieldData; +>text : TextFieldData + + select: SelectFieldData; +>select : SelectFieldData +} + +type FormField = { type: K, data: FieldMap[K] }; +>FormField : FormField +>type : K +>data : FieldMap[K] + +type RenderFunc = (props: FieldMap[K]) => void; +>RenderFunc : RenderFunc +>props : FieldMap[K] + +type RenderFuncMap = { [K in keyof FieldMap]: RenderFunc }; +>RenderFuncMap : RenderFuncMap + +function renderTextField(props: TextFieldData) {} +>renderTextField : (props: TextFieldData) => void +>props : TextFieldData + +function renderSelectField(props: SelectFieldData) {} +>renderSelectField : (props: SelectFieldData) => void +>props : SelectFieldData + +const renderFuncs: RenderFuncMap = { +>renderFuncs : RenderFuncMap +>{ text: renderTextField, select: renderSelectField,} : { text: (props: TextFieldData) => void; select: (props: SelectFieldData) => void; } + + text: renderTextField, +>text : (props: TextFieldData) => void +>renderTextField : (props: TextFieldData) => void + + select: renderSelectField, +>select : (props: SelectFieldData) => void +>renderSelectField : (props: SelectFieldData) => void + +}; + +function renderField(field: FormField) { +>renderField : (field: FormField) => void +>field : FormField + + const renderFn = renderFuncs[field.type]; +>renderFn : RenderFuncMap[K] +>renderFuncs[field.type] : RenderFuncMap[K] +>renderFuncs : RenderFuncMap +>field.type : K +>field : FormField +>type : K + + renderFn(field.data); +>renderFn(field.data) : void +>renderFn : RenderFuncMap[K] +>field.data : FieldMap[K] +>field : FormField +>data : FieldMap[K] +} + +// -------- + +type TypeMap = { +>TypeMap : TypeMap + + foo: string, +>foo : string + + bar: number +>bar : number + +}; + +type Keys = keyof TypeMap; +>Keys : keyof TypeMap + +type HandlerMap = { [P in Keys]: (x: TypeMap[P]) => void }; +>HandlerMap : HandlerMap +>x : TypeMap[P] + +const handlers: HandlerMap = { +>handlers : HandlerMap +>{ foo: s => s.length, bar: n => n.toFixed(2)} : { foo: (s: string) => number; bar: (n: number) => string; } + + foo: s => s.length, +>foo : (s: string) => number +>s => s.length : (s: string) => number +>s : string +>s.length : number +>s : string +>length : number + + bar: n => n.toFixed(2) +>bar : (n: number) => string +>n => n.toFixed(2) : (n: number) => string +>n : number +>n.toFixed(2) : string +>n.toFixed : (fractionDigits?: number | undefined) => string +>n : number +>toFixed : (fractionDigits?: number | undefined) => string +>2 : 2 + +}; + +type DataEntry = { [P in K]: { +>DataEntry : DataEntry + + type: P, +>type : P + + data: TypeMap[P] +>data : TypeMap[P] + +}}[K]; + +const data: DataEntry[] = [ +>data : DataEntry[] +>[ { type: 'foo', data: 'abc' }, { type: 'foo', data: 'def' }, { type: 'bar', data: 42 },] : ({ type: "foo"; data: string; } | { type: "bar"; data: number; })[] + + { type: 'foo', data: 'abc' }, +>{ type: 'foo', data: 'abc' } : { type: "foo"; data: string; } +>type : "foo" +>'foo' : "foo" +>data : string +>'abc' : "abc" + + { type: 'foo', data: 'def' }, +>{ type: 'foo', data: 'def' } : { type: "foo"; data: string; } +>type : "foo" +>'foo' : "foo" +>data : string +>'def' : "def" + + { type: 'bar', data: 42 }, +>{ type: 'bar', data: 42 } : { type: "bar"; data: number; } +>type : "bar" +>'bar' : "bar" +>data : number +>42 : 42 + +]; + +function process(data: DataEntry[]) { +>process : (data: DataEntry[]) => void +>data : DataEntry[] + + data.forEach(block => { +>data.forEach(block => { if (block.type in handlers) { handlers[block.type](block.data) } }) : void +>data.forEach : (callbackfn: (value: DataEntry, index: number, array: DataEntry[]) => void, thisArg?: any) => void +>data : DataEntry[] +>forEach : (callbackfn: (value: DataEntry, index: number, array: DataEntry[]) => void, thisArg?: any) => void +>block => { if (block.type in handlers) { handlers[block.type](block.data) } } : (block: DataEntry) => void +>block : DataEntry + + if (block.type in handlers) { +>block.type in handlers : boolean +>block.type : K +>block : DataEntry +>type : K +>handlers : HandlerMap + + handlers[block.type](block.data) +>handlers[block.type](block.data) : void +>handlers[block.type] : HandlerMap[K] +>handlers : HandlerMap +>block.type : K +>block : DataEntry +>type : K +>block.data : TypeMap[K] +>block : DataEntry +>data : TypeMap[K] + } + }); +} + +process(data); +>process(data) : void +>process : (data: DataEntry[]) => void +>data : DataEntry[] + +process([{ type: 'foo', data: 'abc' }]); +>process([{ type: 'foo', data: 'abc' }]) : void +>process : (data: DataEntry[]) => void +>[{ type: 'foo', data: 'abc' }] : { type: "foo"; data: string; }[] +>{ type: 'foo', data: 'abc' } : { type: "foo"; data: string; } +>type : "foo" +>'foo' : "foo" +>data : string +>'abc' : "abc" + +// -------- + +type LetterMap = { A: string, B: number } +>LetterMap : LetterMap +>A : string +>B : number + +type LetterCaller = { [P in K]: { letter: Record, caller: (x: Record) => void } }[K]; +>LetterCaller : LetterCaller +>letter : Record +>caller : (x: Record) => void +>x : Record + +function call({ letter, caller }: LetterCaller): void { +>call : ({ letter, caller }: LetterCaller) => void +>letter : Record +>caller : (x: Record) => void + + caller(letter); +>caller(letter) : void +>caller : (x: Record) => void +>letter : Record +} + +type A = { A: string }; +>A : A +>A : string + +type B = { B: number }; +>B : B +>B : number + +type ACaller = (a: A) => void; +>ACaller : ACaller +>a : A + +type BCaller = (b: B) => void; +>BCaller : BCaller +>b : B + +declare const xx: { letter: A, caller: ACaller } | { letter: B, caller: BCaller }; +>xx : { letter: A; caller: ACaller; } | { letter: B; caller: BCaller; } +>letter : A +>caller : ACaller +>letter : B +>caller : BCaller + +call(xx); +>call(xx) : void +>call : ({ letter, caller }: LetterCaller) => void +>xx : { letter: A; caller: ACaller; } | { letter: B; caller: BCaller; } + +// -------- + +type Ev = { [P in K]: { +>Ev : Ev + + readonly name: P; +>name : P + + readonly once?: boolean; +>once : boolean | undefined + + readonly callback: (ev: DocumentEventMap[P]) => void; +>callback : (ev: DocumentEventMap[P]) => void +>ev : DocumentEventMap[P] + +}}[K]; + +function processEvents(events: Ev[]) { +>processEvents : (events: Ev[]) => void +>events : Ev[] + + for (const event of events) { +>event : Ev +>events : Ev[] + + document.addEventListener(event.name, (ev) => event.callback(ev), { once: event.once }); +>document.addEventListener(event.name, (ev) => event.callback(ev), { once: event.once }) : void +>document.addEventListener : { (type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void; } +>document : Document +>addEventListener : { (type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions | undefined): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined): void; } +>event.name : K +>event : Ev +>name : K +>(ev) => event.callback(ev) : (this: Document, ev: DocumentEventMap[K]) => void +>ev : DocumentEventMap[K] +>event.callback(ev) : void +>event.callback : (ev: DocumentEventMap[K]) => void +>event : Ev +>callback : (ev: DocumentEventMap[K]) => void +>ev : DocumentEventMap[K] +>{ once: event.once } : { once: boolean | undefined; } +>once : boolean | undefined +>event.once : boolean | undefined +>event : Ev +>once : boolean | undefined + } +} + +function createEventListener({ name, once = false, callback }: Ev): Ev { +>createEventListener : ({ name, once, callback }: Ev) => Ev +>name : K +>once : boolean +>false : false +>callback : (ev: DocumentEventMap[K]) => void + + return { name, once, callback }; +>{ name, once, callback } : { name: K; once: boolean; callback: (ev: DocumentEventMap[K]) => void; } +>name : K +>once : boolean +>callback : (ev: DocumentEventMap[K]) => void +} + +const clickEvent = createEventListener({ +>clickEvent : { readonly name: "click"; readonly once?: boolean | undefined; readonly callback: (ev: MouseEvent) => void; } +>createEventListener({ name: "click", callback: ev => console.log(ev),}) : { readonly name: "click"; readonly once?: boolean | undefined; readonly callback: (ev: MouseEvent) => void; } +>createEventListener : ({ name, once, callback }: Ev) => Ev +>{ name: "click", callback: ev => console.log(ev),} : { name: "click"; callback: (ev: MouseEvent) => void; } + + name: "click", +>name : "click" +>"click" : "click" + + callback: ev => console.log(ev), +>callback : (ev: MouseEvent) => void +>ev => console.log(ev) : (ev: MouseEvent) => void +>ev : MouseEvent +>console.log(ev) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>ev : MouseEvent + +}); + +const scrollEvent = createEventListener({ +>scrollEvent : { readonly name: "scroll"; readonly once?: boolean | undefined; readonly callback: (ev: Event) => void; } +>createEventListener({ name: "scroll", callback: ev => console.log(ev),}) : { readonly name: "scroll"; readonly once?: boolean | undefined; readonly callback: (ev: Event) => void; } +>createEventListener : ({ name, once, callback }: Ev) => Ev +>{ name: "scroll", callback: ev => console.log(ev),} : { name: "scroll"; callback: (ev: Event) => void; } + + name: "scroll", +>name : "scroll" +>"scroll" : "scroll" + + callback: ev => console.log(ev), +>callback : (ev: Event) => void +>ev => console.log(ev) : (ev: Event) => void +>ev : Event +>console.log(ev) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>ev : Event + +}); + +processEvents([clickEvent, scrollEvent]); +>processEvents([clickEvent, scrollEvent]) : void +>processEvents : (events: Ev[]) => void +>[clickEvent, scrollEvent] : ({ readonly name: "click"; readonly once?: boolean | undefined; readonly callback: (ev: MouseEvent) => void; } | { readonly name: "scroll"; readonly once?: boolean | undefined; readonly callback: (ev: Event) => void; })[] +>clickEvent : { readonly name: "click"; readonly once?: boolean | undefined; readonly callback: (ev: MouseEvent) => void; } +>scrollEvent : { readonly name: "scroll"; readonly once?: boolean | undefined; readonly callback: (ev: Event) => void; } + +processEvents([ +>processEvents([ { name: "click", callback: ev => console.log(ev) }, { name: "scroll", callback: ev => console.log(ev) },]) : void +>processEvents : (events: Ev[]) => void +>[ { name: "click", callback: ev => console.log(ev) }, { name: "scroll", callback: ev => console.log(ev) },] : ({ name: "click"; callback: (ev: MouseEvent) => void; } | { name: "scroll"; callback: (ev: Event) => void; })[] + + { name: "click", callback: ev => console.log(ev) }, +>{ name: "click", callback: ev => console.log(ev) } : { name: "click"; callback: (ev: MouseEvent) => void; } +>name : "click" +>"click" : "click" +>callback : (ev: MouseEvent) => void +>ev => console.log(ev) : (ev: MouseEvent) => void +>ev : MouseEvent +>console.log(ev) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>ev : MouseEvent + + { name: "scroll", callback: ev => console.log(ev) }, +>{ name: "scroll", callback: ev => console.log(ev) } : { name: "scroll"; callback: (ev: Event) => void; } +>name : "scroll" +>"scroll" : "scroll" +>callback : (ev: Event) => void +>ev => console.log(ev) : (ev: Event) => void +>ev : Event +>console.log(ev) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>ev : Event + +]); + +// -------- + +function ff1() { +>ff1 : () => void + + type ArgMap = { +>ArgMap : { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; } + + sum: [a: number, b: number], +>sum : [a: number, b: number] + + concat: [a: string, b: string, c: string] +>concat : [a: string, b: string, c: string] + } + type Keys = keyof ArgMap; +>Keys : keyof { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; } + + const funs: { [P in Keys]: (...args: ArgMap[P]) => void } = { +>funs : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; } +>args : { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[P] +>{ sum: (a, b) => a + b, concat: (a, b, c) => a + b + c } : { sum: (a: number, b: number) => number; concat: (a: string, b: string, c: string) => string; } + + sum: (a, b) => a + b, +>sum : (a: number, b: number) => number +>(a, b) => a + b : (a: number, b: number) => number +>a : number +>b : number +>a + b : number +>a : number +>b : number + + concat: (a, b, c) => a + b + c +>concat : (a: string, b: string, c: string) => string +>(a, b, c) => a + b + c : (a: string, b: string, c: string) => string +>a : string +>b : string +>c : string +>a + b + c : string +>a + b : string +>a : string +>b : string +>c : string + } + function apply(funKey: K, ...args: ArgMap[K]) { +>apply : (funKey: K, ...args: { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[K]) => void +>funKey : K +>args : { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[K] + + const fn = funs[funKey]; +>fn : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; }[K] +>funs[funKey] : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; }[K] +>funs : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; } +>funKey : K + + fn(...args); +>fn(...args) : void +>fn : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; }[K] +>...args : string | number +>args : { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[K] + } + const x1 = apply('sum', 1, 2) +>x1 : void +>apply('sum', 1, 2) : void +>apply : (funKey: K, ...args: { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[K]) => void +>'sum' : "sum" +>1 : 1 +>2 : 2 + + const x2 = apply('concat', 'str1', 'str2', 'str3' ) +>x2 : void +>apply('concat', 'str1', 'str2', 'str3' ) : void +>apply : (funKey: K, ...args: { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[K]) => void +>'concat' : "concat" +>'str1' : "str1" +>'str2' : "str2" +>'str3' : "str3" +} + diff --git a/tests/cases/compiler/correlatedUnions.ts b/tests/cases/compiler/correlatedUnions.ts new file mode 100644 index 0000000000000..b0b3aa63faa71 --- /dev/null +++ b/tests/cases/compiler/correlatedUnions.ts @@ -0,0 +1,161 @@ +// @strict: true +// @declaration: true + +// Various repros from #30581 + +type RecordMap = { n: number, s: string, b: boolean }; +type UnionRecord = { [P in K]: { + kind: P, + v: RecordMap[P], + f: (v: RecordMap[P]) => void +}}[K]; + +function processRecord(rec: UnionRecord) { + rec.f(rec.v); +} + +declare const r1: UnionRecord<'n'>; // { kind: 'n', v: number, f: (v: number) => void } +declare const r2: UnionRecord; // { kind: 'n', ... } | { kind: 's', ... } | { kind: 'b', ... } + +processRecord(r1); +processRecord(r2); +processRecord({ kind: 'n', v: 42, f: v => v.toExponential() }); + +// -------- + +type TextFieldData = { value: string } +type SelectFieldData = { options: string[], selectedValue: string } + +type FieldMap = { + text: TextFieldData; + select: SelectFieldData; +} + +type FormField = { type: K, data: FieldMap[K] }; + +type RenderFunc = (props: FieldMap[K]) => void; +type RenderFuncMap = { [K in keyof FieldMap]: RenderFunc }; + +function renderTextField(props: TextFieldData) {} +function renderSelectField(props: SelectFieldData) {} + +const renderFuncs: RenderFuncMap = { + text: renderTextField, + select: renderSelectField, +}; + +function renderField(field: FormField) { + const renderFn = renderFuncs[field.type]; + renderFn(field.data); +} + +// -------- + +type TypeMap = { + foo: string, + bar: number +}; + +type Keys = keyof TypeMap; + +type HandlerMap = { [P in Keys]: (x: TypeMap[P]) => void }; + +const handlers: HandlerMap = { + foo: s => s.length, + bar: n => n.toFixed(2) +}; + +type DataEntry = { [P in K]: { + type: P, + data: TypeMap[P] +}}[K]; + +const data: DataEntry[] = [ + { type: 'foo', data: 'abc' }, + { type: 'foo', data: 'def' }, + { type: 'bar', data: 42 }, +]; + +function process(data: DataEntry[]) { + data.forEach(block => { + if (block.type in handlers) { + handlers[block.type](block.data) + } + }); +} + +process(data); +process([{ type: 'foo', data: 'abc' }]); + +// -------- + +type LetterMap = { A: string, B: number } +type LetterCaller = { [P in K]: { letter: Record, caller: (x: Record) => void } }[K]; + +function call({ letter, caller }: LetterCaller): void { + caller(letter); +} + +type A = { A: string }; +type B = { B: number }; +type ACaller = (a: A) => void; +type BCaller = (b: B) => void; + +declare const xx: { letter: A, caller: ACaller } | { letter: B, caller: BCaller }; + +call(xx); + +// -------- + +type Ev = { [P in K]: { + readonly name: P; + readonly once?: boolean; + readonly callback: (ev: DocumentEventMap[P]) => void; +}}[K]; + +function processEvents(events: Ev[]) { + for (const event of events) { + document.addEventListener(event.name, (ev) => event.callback(ev), { once: event.once }); + } +} + +function createEventListener({ name, once = false, callback }: Ev): Ev { + return { name, once, callback }; +} + +const clickEvent = createEventListener({ + name: "click", + callback: ev => console.log(ev), +}); + +const scrollEvent = createEventListener({ + name: "scroll", + callback: ev => console.log(ev), +}); + +processEvents([clickEvent, scrollEvent]); + +processEvents([ + { name: "click", callback: ev => console.log(ev) }, + { name: "scroll", callback: ev => console.log(ev) }, +]); + +// -------- + +function ff1() { + type ArgMap = { + sum: [a: number, b: number], + concat: [a: string, b: string, c: string] + } + type Keys = keyof ArgMap; + const funs: { [P in Keys]: (...args: ArgMap[P]) => void } = { + sum: (a, b) => a + b, + concat: (a, b, c) => a + b + c + } + function apply(funKey: K, ...args: ArgMap[K]) { + const fn = funs[funKey]; + fn(...args); + } + const x1 = apply('sum', 1, 2) + const x2 = apply('concat', 'str1', 'str2', 'str3' ) +}