-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
} | ||
|
||
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; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, looks like we need to recurse in |
||
|
||
// 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) { | ||
|
@@ -18517,7 +18525,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |
function substituteIndexedMappedType(objectType: MappedType, index: Type) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
|
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) | ||
|
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)) | ||
|
There was a problem hiding this comment.
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?There was a problem hiding this comment.
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.There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.There was a problem hiding this comment.
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?