Skip to content

Add optionality to mapped type indexed access substitutions #57549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14035,15 +14035,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
(declaration.questionToken ? declaration.questionToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeOptional : MappedTypeModifiers.IncludeOptional : 0);
}

// Return -1, 0, or 1, where -1 means optionality is stripped (i.e. -?), 0 means optionality is unchanged, and 1 means
// optionality is added (i.e. +?).
function getMappedTypeOptionality(type: MappedType): number {
const modifiers = getMappedTypeModifiers(type);
return modifiers & MappedTypeModifiers.ExcludeOptional ? -1 : modifiers & MappedTypeModifiers.IncludeOptional ? 1 : 0;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this is computing. What does a result of 2 mean?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result is always either -1, 0, or 1. -1 means optionality is stripped (i.e. -?), 0 means optionality is unchanged, 1 means optionality is added (i.e. +?). When a homomorphic mapped type doesn't modify optionality, we (recursively) consult the optionality of the type you're mapping over to see if it strips or adds optionality.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an enum? Then it'd be easier to tell what the numbers mean.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be an enum, but number feels more appropriate since we use the greater and less than operators to relate the combined values.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a comment about what each number represents work?


function getModifiersTypeOptionality(type: Type): number {
return type.flags & TypeFlags.Intersection ? Math.max(...map((type as IntersectionType).types, getModifiersTypeOptionality)) :
getObjectFlags(type) & ObjectFlags.Mapped ? getCombinedMappedTypeOptionality(type as MappedType) :
0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken this currently falls short here:

type Obj = {
    a: 1,
    b: 2
};

type Identity<T> = { [K in keyof T]: T[K] };

const mapped: {
    [K in keyof Identity<Partial<Obj>>]: Obj[K]
} = {}

const resolveMapped = <K extends keyof typeof mapped>(key: K) => mapped[key].toString(); // should error but it doesnt

Some extra handling of homomorphic mapped types might have to be added here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, looks like we need to recurse in getCombinedMappedTypeOptionality.


// Return -1, 0, or 1, for stripped, unchanged, or added optionality respectively. When a homomorphic mapped type doesn't
// modify optionality, recursively consult the optionality of the type being mapped over to see if it strips or adds optionality.
function getCombinedMappedTypeOptionality(type: MappedType): number {
const optionality = getMappedTypeOptionality(type);
const modifiersType = getModifiersTypeFromMappedType(type);
return optionality || (isGenericMappedType(modifiersType) ? getMappedTypeOptionality(modifiersType) : 0);
return getMappedTypeOptionality(type) || getModifiersTypeOptionality(getModifiersTypeFromMappedType(type));
}

function isPartialMappedType(type: Type) {
Expand Down Expand Up @@ -18517,7 +18525,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function substituteIndexedMappedType(objectType: MappedType, index: Type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking known properties into consideration would fix this inconsistency that this PR creates:

type Obj = {
  a: string;
  b: number;
};

type Obj2 = {
  b: number;
  c: boolean;
};

declare const mapped: {
  [K in keyof (Partial<Obj> & Required<Obj2>)]: number;
};

// displays the same way as `resolved` below!
mapped;
// ^? const mapped: { a?: number | undefined; b: number; c: number; }

const accessMapped = <K extends keyof Obj2>(key: K) => mapped[key].toString();

declare const resolved: { a?: number | undefined; b: number; c: number };

const accessResolved = <K extends keyof Obj2>(key: K) => resolved[key].toString();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, when mapping over intersections constructed from multiple mapped types the PR makes the most conservative assumption about optionality. I think that's an acceptable compromise and I'm concerned with the potential cost of examining every member possibly selected by the key.

const mapper = createTypeMapper([getTypeParameterFromMappedType(objectType)], [index]);
const templateMapper = combineTypeMappers(objectType.mapper, mapper);
return instantiateType(getTemplateTypeFromMappedType(objectType.target as MappedType || objectType), templateMapper);
const instantiatedTemplateType = instantiateType(getTemplateTypeFromMappedType(objectType.target as MappedType || objectType), templateMapper);
return addOptionality(instantiatedTemplateType, /*isProperty*/ true, getCombinedMappedTypeOptionality(objectType) > 0);
}

function getIndexedAccessType(objectType: Type, indexType: Type, accessFlags = AccessFlags.None, accessNode?: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
mappedTypeIndexedAccessConstraint.ts(12,5): error TS18048: 'm1' is possibly 'undefined'.
mappedTypeIndexedAccessConstraint.ts(14,5): error TS18048: 'm2' is possibly 'undefined'.
mappedTypeIndexedAccessConstraint.ts(16,5): error TS18048: 'm3' is possibly 'undefined'.
mappedTypeIndexedAccessConstraint.ts(29,66): error TS2532: Object is possibly 'undefined'.
mappedTypeIndexedAccessConstraint.ts(53,34): error TS2722: Cannot invoke an object which is possibly 'undefined'.


==== mappedTypeIndexedAccessConstraint.ts (5 errors) ====
type Identity<T> = { [K in keyof T]: T[K] };

type M0 = { a: 1, b: 2 };

type M1 = { [K in keyof Partial<M0>]: M0[K] };

type M2 = { [K in keyof Required<M1>]: M1[K] };

type M3 = { [K in keyof Identity<Partial<M0>>]: M0[K] };

function foo<K extends keyof M0>(m1: M1[K], m2: M2[K], m3: M3[K]) {
m1.toString(); // Error
~~
!!! error TS18048: 'm1' is possibly 'undefined'.
m1?.toString();
m2.toString(); // Error
~~
!!! error TS18048: 'm2' is possibly 'undefined'.
m2?.toString();
m3.toString(); // Error
~~
!!! error TS18048: 'm3' is possibly 'undefined'.
m3?.toString();
}

// Repro from #57487

type Obj = {
a: 1,
b: 2
};

const mapped: { [K in keyof Partial<Obj>]: Obj[K] } = {};

const resolveMapped = <K extends keyof typeof mapped>(key: K) => mapped[key].toString(); // Error
~~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.

// Additional repro from #57487

const arr = ["foo", "12", 42] as const;

type Mappings = { foo: boolean, "12": number, 42: string };

type MapperArgs<K extends (typeof arr)[number]> = {
v: K,
i: number
};

type SetOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

type PartMappings = SetOptional<Mappings, "foo">;

const mapper: { [K in keyof PartMappings]: (o: MapperArgs<K>) => PartMappings[K] } = {
foo: ({ v, i }) => v.length + i > 4,
"12": ({ v, i }) => Number(v) + i,
42: ({ v, i }) => `${v}${i}`,
}

const resolveMapper1 = <K extends keyof typeof mapper>(
key: K, o: MapperArgs<K>) => mapper[key](o); // Error
~~~~~~~~~~~
!!! error TS2722: Cannot invoke an object which is possibly 'undefined'.

const resolveMapper2 = <K extends keyof typeof mapper>(
key: K, o: MapperArgs<K>) => mapper[key]?.(o)

227 changes: 227 additions & 0 deletions tests/baselines/reference/mappedTypeIndexedAccessConstraint.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
//// [tests/cases/compiler/mappedTypeIndexedAccessConstraint.ts] ////

=== mappedTypeIndexedAccessConstraint.ts ===
type Identity<T> = { [K in keyof T]: T[K] };
>Identity : Symbol(Identity, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 14))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 22))
>T : Symbol(T, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 14))
>T : Symbol(T, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 14))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 22))

type M0 = { a: 1, b: 2 };
>M0 : Symbol(M0, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 44))
>a : Symbol(a, Decl(mappedTypeIndexedAccessConstraint.ts, 2, 11))
>b : Symbol(b, Decl(mappedTypeIndexedAccessConstraint.ts, 2, 17))

type M1 = { [K in keyof Partial<M0>]: M0[K] };
>M1 : Symbol(M1, Decl(mappedTypeIndexedAccessConstraint.ts, 2, 25))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 4, 13))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>M0 : Symbol(M0, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 44))
>M0 : Symbol(M0, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 44))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 4, 13))

type M2 = { [K in keyof Required<M1>]: M1[K] };
>M2 : Symbol(M2, Decl(mappedTypeIndexedAccessConstraint.ts, 4, 46))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 6, 13))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>M1 : Symbol(M1, Decl(mappedTypeIndexedAccessConstraint.ts, 2, 25))
>M1 : Symbol(M1, Decl(mappedTypeIndexedAccessConstraint.ts, 2, 25))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 6, 13))

type M3 = { [K in keyof Identity<Partial<M0>>]: M0[K] };
>M3 : Symbol(M3, Decl(mappedTypeIndexedAccessConstraint.ts, 6, 47))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 8, 13))
>Identity : Symbol(Identity, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 0))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>M0 : Symbol(M0, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 44))
>M0 : Symbol(M0, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 44))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 8, 13))

function foo<K extends keyof M0>(m1: M1[K], m2: M2[K], m3: M3[K]) {
>foo : Symbol(foo, Decl(mappedTypeIndexedAccessConstraint.ts, 8, 56))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 13))
>M0 : Symbol(M0, Decl(mappedTypeIndexedAccessConstraint.ts, 0, 44))
>m1 : Symbol(m1, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 33))
>M1 : Symbol(M1, Decl(mappedTypeIndexedAccessConstraint.ts, 2, 25))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 13))
>m2 : Symbol(m2, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 43))
>M2 : Symbol(M2, Decl(mappedTypeIndexedAccessConstraint.ts, 4, 46))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 13))
>m3 : Symbol(m3, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 54))
>M3 : Symbol(M3, Decl(mappedTypeIndexedAccessConstraint.ts, 6, 47))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 13))

m1.toString(); // Error
>m1.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>m1 : Symbol(m1, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 33))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))

m1?.toString();
>m1?.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>m1 : Symbol(m1, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 33))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))

m2.toString(); // Error
>m2.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>m2 : Symbol(m2, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 43))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))

m2?.toString();
>m2?.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>m2 : Symbol(m2, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 43))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))

m3.toString(); // Error
>m3.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>m3 : Symbol(m3, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 54))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))

m3?.toString();
>m3?.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>m3 : Symbol(m3, Decl(mappedTypeIndexedAccessConstraint.ts, 10, 54))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
}

// Repro from #57487

type Obj = {
>Obj : Symbol(Obj, Decl(mappedTypeIndexedAccessConstraint.ts, 17, 1))

a: 1,
>a : Symbol(a, Decl(mappedTypeIndexedAccessConstraint.ts, 21, 12))

b: 2
>b : Symbol(b, Decl(mappedTypeIndexedAccessConstraint.ts, 22, 9))

};

const mapped: { [K in keyof Partial<Obj>]: Obj[K] } = {};
>mapped : Symbol(mapped, Decl(mappedTypeIndexedAccessConstraint.ts, 26, 5))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 26, 17))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>Obj : Symbol(Obj, Decl(mappedTypeIndexedAccessConstraint.ts, 17, 1))
>Obj : Symbol(Obj, Decl(mappedTypeIndexedAccessConstraint.ts, 17, 1))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 26, 17))

const resolveMapped = <K extends keyof typeof mapped>(key: K) => mapped[key].toString(); // Error
>resolveMapped : Symbol(resolveMapped, Decl(mappedTypeIndexedAccessConstraint.ts, 28, 5))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 28, 23))
>mapped : Symbol(mapped, Decl(mappedTypeIndexedAccessConstraint.ts, 26, 5))
>key : Symbol(key, Decl(mappedTypeIndexedAccessConstraint.ts, 28, 54))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 28, 23))
>mapped[key].toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
>mapped : Symbol(mapped, Decl(mappedTypeIndexedAccessConstraint.ts, 26, 5))
>key : Symbol(key, Decl(mappedTypeIndexedAccessConstraint.ts, 28, 54))
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))

// Additional repro from #57487

const arr = ["foo", "12", 42] as const;
>arr : Symbol(arr, Decl(mappedTypeIndexedAccessConstraint.ts, 32, 5))
>const : Symbol(const)

type Mappings = { foo: boolean, "12": number, 42: string };
>Mappings : Symbol(Mappings, Decl(mappedTypeIndexedAccessConstraint.ts, 32, 39))
>foo : Symbol(foo, Decl(mappedTypeIndexedAccessConstraint.ts, 34, 17))
>"12" : Symbol("12", Decl(mappedTypeIndexedAccessConstraint.ts, 34, 31))
>42 : Symbol(42, Decl(mappedTypeIndexedAccessConstraint.ts, 34, 45))

type MapperArgs<K extends (typeof arr)[number]> = {
>MapperArgs : Symbol(MapperArgs, Decl(mappedTypeIndexedAccessConstraint.ts, 34, 59))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 36, 16))
>arr : Symbol(arr, Decl(mappedTypeIndexedAccessConstraint.ts, 32, 5))

v: K,
>v : Symbol(v, Decl(mappedTypeIndexedAccessConstraint.ts, 36, 51))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 36, 16))

i: number
>i : Symbol(i, Decl(mappedTypeIndexedAccessConstraint.ts, 37, 9))

};

type SetOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
>SetOptional : Symbol(SetOptional, Decl(mappedTypeIndexedAccessConstraint.ts, 39, 2))
>T : Symbol(T, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 17))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 19))
>T : Symbol(T, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 17))
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 17))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 19))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 17))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 19))

type PartMappings = SetOptional<Mappings, "foo">;
>PartMappings : Symbol(PartMappings, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 74))
>SetOptional : Symbol(SetOptional, Decl(mappedTypeIndexedAccessConstraint.ts, 39, 2))
>Mappings : Symbol(Mappings, Decl(mappedTypeIndexedAccessConstraint.ts, 32, 39))

const mapper: { [K in keyof PartMappings]: (o: MapperArgs<K>) => PartMappings[K] } = {
>mapper : Symbol(mapper, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 5))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 17))
>PartMappings : Symbol(PartMappings, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 74))
>o : Symbol(o, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 44))
>MapperArgs : Symbol(MapperArgs, Decl(mappedTypeIndexedAccessConstraint.ts, 34, 59))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 17))
>PartMappings : Symbol(PartMappings, Decl(mappedTypeIndexedAccessConstraint.ts, 41, 74))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 17))

foo: ({ v, i }) => v.length + i > 4,
>foo : Symbol(foo, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 86))
>v : Symbol(v, Decl(mappedTypeIndexedAccessConstraint.ts, 46, 11))
>i : Symbol(i, Decl(mappedTypeIndexedAccessConstraint.ts, 46, 14))
>v.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>v : Symbol(v, Decl(mappedTypeIndexedAccessConstraint.ts, 46, 11))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>i : Symbol(i, Decl(mappedTypeIndexedAccessConstraint.ts, 46, 14))

"12": ({ v, i }) => Number(v) + i,
>"12" : Symbol("12", Decl(mappedTypeIndexedAccessConstraint.ts, 46, 40))
>v : Symbol(v, Decl(mappedTypeIndexedAccessConstraint.ts, 47, 12))
>i : Symbol(i, Decl(mappedTypeIndexedAccessConstraint.ts, 47, 15))
>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>v : Symbol(v, Decl(mappedTypeIndexedAccessConstraint.ts, 47, 12))
>i : Symbol(i, Decl(mappedTypeIndexedAccessConstraint.ts, 47, 15))

42: ({ v, i }) => `${v}${i}`,
>42 : Symbol(42, Decl(mappedTypeIndexedAccessConstraint.ts, 47, 38))
>v : Symbol(v, Decl(mappedTypeIndexedAccessConstraint.ts, 48, 10))
>i : Symbol(i, Decl(mappedTypeIndexedAccessConstraint.ts, 48, 13))
>v : Symbol(v, Decl(mappedTypeIndexedAccessConstraint.ts, 48, 10))
>i : Symbol(i, Decl(mappedTypeIndexedAccessConstraint.ts, 48, 13))
}

const resolveMapper1 = <K extends keyof typeof mapper>(
>resolveMapper1 : Symbol(resolveMapper1, Decl(mappedTypeIndexedAccessConstraint.ts, 51, 5))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 51, 24))
>mapper : Symbol(mapper, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 5))

key: K, o: MapperArgs<K>) => mapper[key](o); // Error
>key : Symbol(key, Decl(mappedTypeIndexedAccessConstraint.ts, 51, 55))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 51, 24))
>o : Symbol(o, Decl(mappedTypeIndexedAccessConstraint.ts, 52, 11))
>MapperArgs : Symbol(MapperArgs, Decl(mappedTypeIndexedAccessConstraint.ts, 34, 59))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 51, 24))
>mapper : Symbol(mapper, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 5))
>key : Symbol(key, Decl(mappedTypeIndexedAccessConstraint.ts, 51, 55))
>o : Symbol(o, Decl(mappedTypeIndexedAccessConstraint.ts, 52, 11))

const resolveMapper2 = <K extends keyof typeof mapper>(
>resolveMapper2 : Symbol(resolveMapper2, Decl(mappedTypeIndexedAccessConstraint.ts, 54, 5))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 54, 24))
>mapper : Symbol(mapper, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 5))

key: K, o: MapperArgs<K>) => mapper[key]?.(o)
>key : Symbol(key, Decl(mappedTypeIndexedAccessConstraint.ts, 54, 55))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 54, 24))
>o : Symbol(o, Decl(mappedTypeIndexedAccessConstraint.ts, 55, 11))
>MapperArgs : Symbol(MapperArgs, Decl(mappedTypeIndexedAccessConstraint.ts, 34, 59))
>K : Symbol(K, Decl(mappedTypeIndexedAccessConstraint.ts, 54, 24))
>mapper : Symbol(mapper, Decl(mappedTypeIndexedAccessConstraint.ts, 45, 5))
>key : Symbol(key, Decl(mappedTypeIndexedAccessConstraint.ts, 54, 55))
>o : Symbol(o, Decl(mappedTypeIndexedAccessConstraint.ts, 55, 11))

Loading
Loading