Skip to content

Commit 1f3304f

Browse files
authored
Port "Preserve type parameter constraint in emitted mapped types while preserving their distributivity" (#1834)
1 parent ad297a9 commit 1f3304f

21 files changed

+65
-314
lines changed

internal/checker/nodebuilderimpl.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,25 +1367,26 @@ func (b *nodeBuilderImpl) createMappedTypeNodeFromType(t *Type) *ast.TypeNode {
13671367
}
13681368
var appropriateConstraintTypeNode *ast.Node
13691369
var newTypeVariable *ast.Node
1370+
templateType := b.ch.getTemplateTypeFromMappedType(t)
1371+
typeParameter := b.ch.getTypeParameterFromMappedType(t)
13701372

13711373
// If the mapped type isn't `keyof` constraint-declared, _but_ still has modifiers preserved, and its naive instantiation won't preserve modifiers because its constraint isn't `keyof` constrained, we have work to do
13721374
needsModifierPreservingWrapper := !b.ch.isMappedTypeWithKeyofConstraintDeclaration(t) &&
13731375
b.ch.getModifiersTypeFromMappedType(t).flags&TypeFlagsUnknown == 0 &&
13741376
b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 &&
13751377
!(b.ch.getConstraintTypeFromMappedType(t).flags&TypeFlagsTypeParameter != 0 && b.ch.getConstraintOfTypeParameter(b.ch.getConstraintTypeFromMappedType(t)).flags&TypeFlagsIndex != 0)
13761378

1377-
cleanup := b.enterNewScope(mapped.declaration.AsNode(), nil, []*Type{b.ch.getTypeParameterFromMappedType(t)}, nil, nil)
1378-
defer cleanup()
1379-
13801379
if b.ch.isMappedTypeWithKeyofConstraintDeclaration(t) {
13811380
// We have a { [P in keyof T]: X }
13821381
// We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType`
13831382
if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) {
1384-
newParam := b.ch.newTypeParameter(
1383+
newConstraintParam := b.ch.newTypeParameter(
13851384
b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"),
13861385
)
1387-
name := b.typeParameterToName(newParam)
1386+
name := b.typeParameterToName(newConstraintParam)
1387+
target := t.Target()
13881388
newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil)
1389+
templateType = b.ch.instantiateType(b.ch.getTemplateTypeFromMappedType(target), newTypeMapper([]*Type{b.ch.getTypeParameterFromMappedType(target), b.ch.getModifiersTypeFromMappedType(target)}, []*Type{typeParameter, newConstraintParam}))
13891390
}
13901391
indexTarget := newTypeVariable
13911392
if indexTarget == nil {
@@ -1405,15 +1406,18 @@ func (b *nodeBuilderImpl) createMappedTypeNodeFromType(t *Type) *ast.TypeNode {
14051406
appropriateConstraintTypeNode = b.typeToTypeNode(b.ch.getConstraintTypeFromMappedType(t))
14061407
}
14071408

1408-
typeParameterNode := b.typeParameterToDeclarationWithConstraint(b.ch.getTypeParameterFromMappedType(t), appropriateConstraintTypeNode)
1409+
// nameType and templateType nodes have to be in the new scope
1410+
cleanup := b.enterNewScope(mapped.declaration.AsNode(), nil, []*Type{b.ch.getTypeParameterFromMappedType(t)}, nil, nil)
1411+
typeParameterNode := b.typeParameterToDeclarationWithConstraint(typeParameter, appropriateConstraintTypeNode)
14091412
var nameTypeNode *ast.Node
14101413
if mapped.declaration.NameType != nil {
14111414
nameTypeNode = b.typeToTypeNode(b.ch.getNameTypeFromMappedType(t))
14121415
}
14131416
templateTypeNode := b.typeToTypeNode(b.ch.removeMissingType(
1414-
b.ch.getTemplateTypeFromMappedType(t),
1417+
templateType,
14151418
getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0,
14161419
))
1420+
cleanup()
14171421
result := b.f.NewMappedTypeNode(
14181422
readonlyToken,
14191423
typeParameterNode,

testdata/baselines/reference/submodule/compiler/computedTypesKeyofNoIndexSignatureType.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
=== computedTypesKeyofNoIndexSignatureType.ts ===
44
type Compute<A> = { [K in keyof A]: Compute<A[K]>; } & {};
5-
>Compute : { [K in keyof A]: A[K] extends infer T ? { [K_1 in keyof T]: A[K][K_1] extends infer T_1 ? { [K_2 in keyof T_1]: A[K][K_1][K_2] extends infer T_2 ? { [K_3 in keyof T_2]: A[K][K_1][K_2][K_3] extends infer T_3 ? { [K_4 in keyof T_3]: A[K][K_1][K_2][K_3][K_4] extends infer T_4 ? { [K_5 in keyof T_4]: A[K][K_1][K_2][K_3][K_4][K_5] extends infer T_5 ? { [K_6 in keyof T_5]: A[K][K_1][K_2][K_3][K_4][K_5][K_6] extends infer T_6 ? { [K_7 in keyof T_6]: A[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7] extends infer T_7 ? { [K_8 in keyof T_7]: A[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8] extends infer T_8 ? { [K_9 in keyof T_8]: A[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9] extends infer T_9 ? { [K_10 in keyof T_9]: any; } : never; } : never; } : never; } : never; } : never; } : never; } : never; } : never; } : never; } : never; }
5+
>Compute : { [K in keyof A]: A[K] extends infer T ? { [K_1 in keyof T]: T[K_1] extends infer T_1 ? { [K_2 in keyof T_1]: T_1[K_2] extends infer T_2 ? { [K_3 in keyof T_2]: T_2[K_3] extends infer T_3 ? { [K_4 in keyof T_3]: T_3[K_4] extends infer T_4 ? { [K_5 in keyof T_4]: T_4[K_5] extends infer T_5 ? { [K_6 in keyof T_5]: T_5[K_6] extends infer T_6 ? { [K_7 in keyof T_6]: T_6[K_7] extends infer T_7 ? { [K_8 in keyof T_7]: T_7[K_8] extends infer T_8 ? { [K_9 in keyof T_8]: T_8[K_9] extends infer T_9 ? { [K_10 in keyof T_9]: any; } : never; } : never; } : never; } : never; } : never; } : never; } : never; } : never; } : never; } : never; }
66

77
type EqualsTest<T> = <A>() => A extends T ? 1 : 0;
88
>EqualsTest : EqualsTest<T>

testdata/baselines/reference/submodule/compiler/computedTypesKeyofNoIndexSignatureType.types.diff

Lines changed: 0 additions & 11 deletions
This file was deleted.

testdata/baselines/reference/submodule/compiler/declarationEmitMappedTypeDistributivityPreservesConstraints.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ declare const _default: {
5252
fn: <T extends {
5353
x: T["x"] extends infer T_1 extends {
5454
[x: string]: (...params: unknown[]) => unknown;
55-
} ? { [K in keyof T_1]: T["x"][K]; } : never;
56-
}>(sliceIndex: T) => T["x"] extends infer T_1 extends {
55+
} ? { [K in keyof T_1]: T_1[K]; } : never;
56+
}>(sliceIndex: T) => T["x"] extends infer T_2 extends {
5757
[x: string]: (...params: unknown[]) => unknown;
58-
} ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never;
58+
} ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never;
5959
};
6060
};
6161
export default _default;

testdata/baselines/reference/submodule/compiler/declarationEmitMappedTypeDistributivityPreservesConstraints.js.diff

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,12 @@
99
exports.default = { test: types_1.default };
1010

1111

12-
@@= skipped -25, +25 lines =@@
13-
fn: <T extends {
14-
x: T["x"] extends infer T_1 extends {
15-
[x: string]: (...params: unknown[]) => unknown;
16-
- } ? { [K in keyof T_1]: T_1[K]; } : never;
17-
- }>(sliceIndex: T) => T["x"] extends infer T_2 extends {
18-
+ } ? { [K in keyof T_1]: T["x"][K]; } : never;
19-
+ }>(sliceIndex: T) => T["x"] extends infer T_1 extends {
12+
@@= skipped -28, +28 lines =@@
13+
} ? { [K in keyof T_1]: T_1[K]; } : never;
14+
}>(sliceIndex: T) => T["x"] extends infer T_2 extends {
2015
[x: string]: (...params: unknown[]) => unknown;
2116
- } ? { [K_1 in keyof T_2]: Parameters<T_2[K_1]>; } : never;
22-
+ } ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never;
17+
+ } ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never;
2318
};
2419
};
2520
export default _default;

testdata/baselines/reference/submodule/compiler/declarationEmitMappedTypeDistributivityPreservesConstraints.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ export default { fn };
2626

2727
=== reexport.ts ===
2828
import test from "./types";
29-
>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T["x"][K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never; }
29+
>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never; }
3030

3131
export default { test };
32-
>{ test } : { test: { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T["x"][K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never; }; }
33-
>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T["x"][K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never; }
32+
>{ test } : { test: { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never; }; }
33+
>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never; }
3434

testdata/baselines/reference/submodule/compiler/declarationEmitMappedTypeDistributivityPreservesConstraints.types.diff

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
=== reexport.ts ===
66
import test from "./types";
77
->test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K_1 in keyof T_2]: Parameters<T_2[K_1]>; } : never; }
8-
+>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T["x"][K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never; }
8+
+>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never; }
99

1010
export default { test };
1111
->{ test } : { test: { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K_1 in keyof T_2]: Parameters<T_2[K_1]>; } : never; }; }
1212
->test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K_1 in keyof T_2]: Parameters<T_2[K_1]>; } : never; }
13-
+>{ test } : { test: { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T["x"][K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never; }; }
14-
+>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T["x"][K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: Parameters<T["x"][K]>; } : never; }
13+
+>{ test } : { test: { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never; }; }
14+
+>test : { fn: <T extends { x: T["x"] extends infer T_1 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_1]: T_1[K]; } : never; }>(sliceIndex: T) => T["x"] extends infer T_2 extends { [x: string]: (...params: unknown[]) => unknown; } ? { [K in keyof T_2]: Parameters<T_2[K]>; } : never; }

testdata/baselines/reference/submodule/compiler/declarationEmitMappedTypePreservesTypeParameterConstraint.js

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -94,67 +94,13 @@ export declare type ZodRawShape = {
9494
};
9595
export declare const buildSchema: <V extends string>(version: V) => addQuestionMarks<baseObjectOutputType<{
9696
version: ZodLiteral<V>;
97-
}>, undefined extends V ? never : "version"> extends infer T ? { [k in keyof T]: addQuestionMarks<baseObjectOutputType<{
98-
version: ZodLiteral<V>;
99-
}>, undefined extends V ? never : "version">[k]; } : never;
97+
}>, undefined extends V ? never : "version"> extends infer T ? { [k in keyof T]: T[k]; } : never;
10098
type evaluate<t> = {
10199
[k in keyof t]: t[k];
102100
} & unknown;
103101
export type entryOf<o> = evaluate<{
104102
[k in keyof o]-?: [k, o[k] & ({} | null)];
105103
}[o extends readonly unknown[] ? keyof o & number : keyof o]>;
106104
export type entriesOf<o extends object> = evaluate<entryOf<o>[]>;
107-
export declare const entriesOf: <o extends object>(o: o) => ({ [k_1 in keyof o]-?: [k_1, o[k_1] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o] extends infer T ? { [k in keyof T]: { [k_1 in keyof o]-?: [k_1, o[k_1] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o][k]; } : never)[];
105+
export declare const entriesOf: <o extends object>(o: o) => ({ [k in keyof o]-?: [k, o[k] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o] extends infer T ? { [k in keyof T]: T[k]; } : never)[];
108106
export {};
109-
110-
111-
//// [DtsFileErrors]
112-
113-
114-
declarationEmitMappedTypePreservesTypeParameterConstraint.d.ts(24,82): error TS2536: Type 'k' cannot be used to index type 'addQuestionMarks<baseObjectOutputType<{ version: ZodLiteral<V>; }>, undefined extends V ? never : "version">'.
115-
declarationEmitMappedTypePreservesTypeParameterConstraint.d.ts(34,210): error TS2536: Type 'k' cannot be used to index type '{ [k_1 in keyof o]-?: [k_1, o[k_1] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o]'.
116-
117-
118-
==== declarationEmitMappedTypePreservesTypeParameterConstraint.d.ts (2 errors) ====
119-
declare type requiredKeys<T extends object> = {
120-
[k in keyof T]: undefined extends T[k] ? never : k;
121-
}[keyof T];
122-
declare type addQuestionMarks<T extends object, R extends keyof T = requiredKeys<T>> = Pick<Required<T>, R> & Partial<T>;
123-
declare type identity<T> = T;
124-
declare type flatten<T> = identity<{
125-
[k in keyof T]: T[k];
126-
}>;
127-
export declare abstract class ZodType<Output = any> {
128-
readonly _output: Output;
129-
}
130-
export declare class ZodLiteral<T> extends ZodType<T> {
131-
}
132-
export declare type ZodTypeAny = ZodType<any>;
133-
export declare type baseObjectOutputType<Shape extends ZodRawShape> = {
134-
[k in keyof Shape]: Shape[k]["_output"];
135-
};
136-
export declare type objectOutputType<Shape extends ZodRawShape> = flatten<addQuestionMarks<baseObjectOutputType<Shape>>>;
137-
export declare type ZodRawShape = {
138-
[k: string]: ZodTypeAny;
139-
};
140-
export declare const buildSchema: <V extends string>(version: V) => addQuestionMarks<baseObjectOutputType<{
141-
version: ZodLiteral<V>;
142-
}>, undefined extends V ? never : "version"> extends infer T ? { [k in keyof T]: addQuestionMarks<baseObjectOutputType<{
143-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
144-
version: ZodLiteral<V>;
145-
~~~~~~~~~~~~~~~~~~~~~~~~~~~
146-
}>, undefined extends V ? never : "version">[k]; } : never;
147-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148-
!!! error TS2536: Type 'k' cannot be used to index type 'addQuestionMarks<baseObjectOutputType<{ version: ZodLiteral<V>; }>, undefined extends V ? never : "version">'.
149-
type evaluate<t> = {
150-
[k in keyof t]: t[k];
151-
} & unknown;
152-
export type entryOf<o> = evaluate<{
153-
[k in keyof o]-?: [k, o[k] & ({} | null)];
154-
}[o extends readonly unknown[] ? keyof o & number : keyof o]>;
155-
export type entriesOf<o extends object> = evaluate<entryOf<o>[]>;
156-
export declare const entriesOf: <o extends object>(o: o) => ({ [k_1 in keyof o]-?: [k_1, o[k_1] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o] extends infer T ? { [k in keyof T]: { [k_1 in keyof o]-?: [k_1, o[k_1] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o][k]; } : never)[];
157-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
158-
!!! error TS2536: Type 'k' cannot be used to index type '{ [k_1 in keyof o]-?: [k_1, o[k_1] & ({} | null)]; }[o extends readonly unknown[] ? keyof o & number : keyof o]'.
159-
export {};
160-

0 commit comments

Comments
 (0)