Fix multiple issues with indexed access types applied to mapped types (#47109)

* Fix multiple issues with indexed access types applied to mapped types

* Add tests
This commit is contained in:
Anders Hejlsberg
2021-12-14 11:51:03 -08:00
committed by GitHub
parent 7e0e8671fa
commit 3d3825e783
5 changed files with 1667 additions and 19 deletions
+30 -19
View File
@@ -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) {
@@ -0,0 +1,350 @@
//// [correlatedUnions.ts]
// Various repros from #30581
type RecordMap = { n: number, s: string, b: boolean };
type UnionRecord<K extends keyof RecordMap = keyof RecordMap> = { [P in K]: {
kind: P,
v: RecordMap[P],
f: (v: RecordMap[P]) => void
}}[K];
function processRecord<K extends keyof RecordMap>(rec: UnionRecord<K>) {
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<K extends keyof FieldMap> = { type: K, data: FieldMap[K] };
type RenderFunc<K extends keyof FieldMap> = (props: FieldMap[K]) => void;
type RenderFuncMap = { [K in keyof FieldMap]: RenderFunc<K> };
function renderTextField(props: TextFieldData) {}
function renderSelectField(props: SelectFieldData) {}
const renderFuncs: RenderFuncMap = {
text: renderTextField,
select: renderSelectField,
};
function renderField<K extends keyof FieldMap>(field: FormField<K>) {
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<K extends Keys = Keys> = { [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<K extends Keys>(data: DataEntry<K>[]) {
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<K extends keyof LetterMap> = { [P in K]: { letter: Record<P, LetterMap[P]>, caller: (x: Record<P, LetterMap[P]>) => void } }[K];
function call<K extends keyof LetterMap>({ letter, caller }: LetterCaller<K>): 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<K extends keyof DocumentEventMap> = { [P in K]: {
readonly name: P;
readonly once?: boolean;
readonly callback: (ev: DocumentEventMap[P]) => void;
}}[K];
function processEvents<K extends keyof DocumentEventMap>(events: Ev<K>[]) {
for (const event of events) {
document.addEventListener(event.name, (ev) => event.callback(ev), { once: event.once });
}
}
function createEventListener<K extends keyof DocumentEventMap>({ name, once = false, callback }: Ev<K>): Ev<K> {
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<K extends Keys>(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<K extends keyof RecordMap = keyof RecordMap> = {
[P in K]: {
kind: P;
v: RecordMap[P];
f: (v: RecordMap[P]) => void;
};
}[K];
declare function processRecord<K extends keyof RecordMap>(rec: UnionRecord<K>): 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<K extends keyof FieldMap> = {
type: K;
data: FieldMap[K];
};
declare type RenderFunc<K extends keyof FieldMap> = (props: FieldMap[K]) => void;
declare type RenderFuncMap = {
[K in keyof FieldMap]: RenderFunc<K>;
};
declare function renderTextField(props: TextFieldData): void;
declare function renderSelectField(props: SelectFieldData): void;
declare const renderFuncs: RenderFuncMap;
declare function renderField<K extends keyof FieldMap>(field: FormField<K>): 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<K extends Keys = Keys> = {
[P in K]: {
type: P;
data: TypeMap[P];
};
}[K];
declare const data: DataEntry[];
declare function process<K extends Keys>(data: DataEntry<K>[]): void;
declare type LetterMap = {
A: string;
B: number;
};
declare type LetterCaller<K extends keyof LetterMap> = {
[P in K]: {
letter: Record<P, LetterMap[P]>;
caller: (x: Record<P, LetterMap[P]>) => void;
};
}[K];
declare function call<K extends keyof LetterMap>({ letter, caller }: LetterCaller<K>): 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<K extends keyof DocumentEventMap> = {
[P in K]: {
readonly name: P;
readonly once?: boolean;
readonly callback: (ev: DocumentEventMap[P]) => void;
};
}[K];
declare function processEvents<K extends keyof DocumentEventMap>(events: Ev<K>[]): void;
declare function createEventListener<K extends keyof DocumentEventMap>({ name, once, callback }: Ev<K>): Ev<K>;
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;
@@ -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<K extends keyof RecordMap = keyof RecordMap> = { [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<K extends keyof RecordMap>(rec: UnionRecord<K>) {
>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<K extends keyof FieldMap> = { 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<K extends keyof FieldMap> = (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<K> };
>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<K extends keyof FieldMap>(field: FormField<K>) {
>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<K extends Keys = Keys> = { [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<K extends Keys>(data: DataEntry<K>[]) {
>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<K extends keyof LetterMap> = { [P in K]: { letter: Record<P, LetterMap[P]>, caller: (x: Record<P, LetterMap[P]>) => 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<K extends keyof LetterMap>({ letter, caller }: LetterCaller<K>): 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<K extends keyof DocumentEventMap> = { [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<K extends keyof DocumentEventMap>(events: Ev<K>[]) {
>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<K extends keyof DocumentEventMap>({ name, once = false, callback }: Ev<K>): Ev<K> {
>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<K extends Keys>(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))
}
@@ -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<K extends keyof RecordMap = keyof RecordMap> = { [P in K]: {
>UnionRecord : UnionRecord<K>
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<K extends keyof RecordMap>(rec: UnionRecord<K>) {
>processRecord : <K extends keyof RecordMap>(rec: UnionRecord<K>) => void
>rec : UnionRecord<K>
rec.f(rec.v);
>rec.f(rec.v) : void
>rec.f : (v: RecordMap[K]) => void
>rec : UnionRecord<K>
>f : (v: RecordMap[K]) => void
>rec.v : RecordMap[K]
>rec : UnionRecord<K>
>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<keyof RecordMap>
processRecord(r1);
>processRecord(r1) : void
>processRecord : <K extends keyof RecordMap>(rec: UnionRecord<K>) => void
>r1 : { kind: "n"; v: number; f: (v: number) => void; }
processRecord(r2);
>processRecord(r2) : void
>processRecord : <K extends keyof RecordMap>(rec: UnionRecord<K>) => void
>r2 : UnionRecord<keyof RecordMap>
processRecord({ kind: 'n', v: 42, f: v => v.toExponential() });
>processRecord({ kind: 'n', v: 42, f: v => v.toExponential() }) : void
>processRecord : <K extends keyof RecordMap>(rec: UnionRecord<K>) => 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<K extends keyof FieldMap> = { type: K, data: FieldMap[K] };
>FormField : FormField<K>
>type : K
>data : FieldMap[K]
type RenderFunc<K extends keyof FieldMap> = (props: FieldMap[K]) => void;
>RenderFunc : RenderFunc<K>
>props : FieldMap[K]
type RenderFuncMap = { [K in keyof FieldMap]: RenderFunc<K> };
>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<K extends keyof FieldMap>(field: FormField<K>) {
>renderField : <K extends keyof FieldMap>(field: FormField<K>) => void
>field : FormField<K>
const renderFn = renderFuncs[field.type];
>renderFn : RenderFuncMap[K]
>renderFuncs[field.type] : RenderFuncMap[K]
>renderFuncs : RenderFuncMap
>field.type : K
>field : FormField<K>
>type : K
renderFn(field.data);
>renderFn(field.data) : void
>renderFn : RenderFuncMap[K]
>field.data : FieldMap[K]
>field : FormField<K>
>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<K extends Keys = Keys> = { [P in K]: {
>DataEntry : DataEntry<K>
type: P,
>type : P
data: TypeMap[P]
>data : TypeMap[P]
}}[K];
const data: DataEntry[] = [
>data : DataEntry<keyof TypeMap>[]
>[ { 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<K extends Keys>(data: DataEntry<K>[]) {
>process : <K extends keyof TypeMap>(data: DataEntry<K>[]) => void
>data : DataEntry<K>[]
data.forEach(block => {
>data.forEach(block => { if (block.type in handlers) { handlers[block.type](block.data) } }) : void
>data.forEach : (callbackfn: (value: DataEntry<K>, index: number, array: DataEntry<K>[]) => void, thisArg?: any) => void
>data : DataEntry<K>[]
>forEach : (callbackfn: (value: DataEntry<K>, index: number, array: DataEntry<K>[]) => void, thisArg?: any) => void
>block => { if (block.type in handlers) { handlers[block.type](block.data) } } : (block: DataEntry<K>) => void
>block : DataEntry<K>
if (block.type in handlers) {
>block.type in handlers : boolean
>block.type : K
>block : DataEntry<K>
>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<K>
>type : K
>block.data : TypeMap[K]
>block : DataEntry<K>
>data : TypeMap[K]
}
});
}
process(data);
>process(data) : void
>process : <K extends keyof TypeMap>(data: DataEntry<K>[]) => void
>data : DataEntry<keyof TypeMap>[]
process([{ type: 'foo', data: 'abc' }]);
>process([{ type: 'foo', data: 'abc' }]) : void
>process : <K extends keyof TypeMap>(data: DataEntry<K>[]) => 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<K extends keyof LetterMap> = { [P in K]: { letter: Record<P, LetterMap[P]>, caller: (x: Record<P, LetterMap[P]>) => void } }[K];
>LetterCaller : LetterCaller<K>
>letter : Record<P, LetterMap[P]>
>caller : (x: Record<P, LetterMap[P]>) => void
>x : Record<P, LetterMap[P]>
function call<K extends keyof LetterMap>({ letter, caller }: LetterCaller<K>): void {
>call : <K extends keyof LetterMap>({ letter, caller }: LetterCaller<K>) => void
>letter : Record<K, LetterMap[K]>
>caller : (x: Record<K, LetterMap[K]>) => void
caller(letter);
>caller(letter) : void
>caller : (x: Record<K, LetterMap[K]>) => void
>letter : Record<K, LetterMap[K]>
}
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 : <K extends keyof LetterMap>({ letter, caller }: LetterCaller<K>) => void
>xx : { letter: A; caller: ACaller; } | { letter: B; caller: BCaller; }
// --------
type Ev<K extends keyof DocumentEventMap> = { [P in K]: {
>Ev : Ev<K>
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<K extends keyof DocumentEventMap>(events: Ev<K>[]) {
>processEvents : <K extends keyof DocumentEventMap>(events: Ev<K>[]) => void
>events : Ev<K>[]
for (const event of events) {
>event : Ev<K>
>events : Ev<K>[]
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 : { <K extends keyof DocumentEventMap>(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 : { <K extends keyof DocumentEventMap>(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<K>
>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<K>
>callback : (ev: DocumentEventMap[K]) => void
>ev : DocumentEventMap[K]
>{ once: event.once } : { once: boolean | undefined; }
>once : boolean | undefined
>event.once : boolean | undefined
>event : Ev<K>
>once : boolean | undefined
}
}
function createEventListener<K extends keyof DocumentEventMap>({ name, once = false, callback }: Ev<K>): Ev<K> {
>createEventListener : <K extends keyof DocumentEventMap>({ name, once, callback }: Ev<K>) => Ev<K>
>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 : <K extends keyof DocumentEventMap>({ name, once, callback }: Ev<K>) => Ev<K>
>{ 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 : <K extends keyof DocumentEventMap>({ name, once, callback }: Ev<K>) => Ev<K>
>{ 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 : <K extends keyof DocumentEventMap>(events: Ev<K>[]) => 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 : <K extends keyof DocumentEventMap>(events: Ev<K>[]) => 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<K extends Keys>(funKey: K, ...args: ArgMap[K]) {
>apply : <K extends keyof { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }>(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 : <K extends keyof { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }>(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 : <K extends keyof { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }>(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"
}
+161
View File
@@ -0,0 +1,161 @@
// @strict: true
// @declaration: true
// Various repros from #30581
type RecordMap = { n: number, s: string, b: boolean };
type UnionRecord<K extends keyof RecordMap = keyof RecordMap> = { [P in K]: {
kind: P,
v: RecordMap[P],
f: (v: RecordMap[P]) => void
}}[K];
function processRecord<K extends keyof RecordMap>(rec: UnionRecord<K>) {
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<K extends keyof FieldMap> = { type: K, data: FieldMap[K] };
type RenderFunc<K extends keyof FieldMap> = (props: FieldMap[K]) => void;
type RenderFuncMap = { [K in keyof FieldMap]: RenderFunc<K> };
function renderTextField(props: TextFieldData) {}
function renderSelectField(props: SelectFieldData) {}
const renderFuncs: RenderFuncMap = {
text: renderTextField,
select: renderSelectField,
};
function renderField<K extends keyof FieldMap>(field: FormField<K>) {
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<K extends Keys = Keys> = { [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<K extends Keys>(data: DataEntry<K>[]) {
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<K extends keyof LetterMap> = { [P in K]: { letter: Record<P, LetterMap[P]>, caller: (x: Record<P, LetterMap[P]>) => void } }[K];
function call<K extends keyof LetterMap>({ letter, caller }: LetterCaller<K>): 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<K extends keyof DocumentEventMap> = { [P in K]: {
readonly name: P;
readonly once?: boolean;
readonly callback: (ev: DocumentEventMap[P]) => void;
}}[K];
function processEvents<K extends keyof DocumentEventMap>(events: Ev<K>[]) {
for (const event of events) {
document.addEventListener(event.name, (ev) => event.callback(ev), { once: event.once });
}
}
function createEventListener<K extends keyof DocumentEventMap>({ name, once = false, callback }: Ev<K>): Ev<K> {
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<K extends Keys>(funKey: K, ...args: ArgMap[K]) {
const fn = funs[funKey];
fn(...args);
}
const x1 = apply('sum', 1, 2)
const x2 = apply('concat', 'str1', 'str2', 'str3' )
}