Skip to content

Commit 17c7c26

Browse files
authored
Properly preserve modifiers in homomorphic mapped types with 'as' clauses (microsoft#40633)
* Use original property name to fetch source property for modifiers * Add regression test * Accept new baselines
1 parent 8cdf5a2 commit 17c7c26

File tree

5 files changed

+93
-1
lines changed

5 files changed

+93
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10458,7 +10458,7 @@ namespace ts {
1045810458
existingProp.keyType = getUnionType([existingProp.keyType, keyType]);
1045910459
}
1046010460
else {
10461-
const modifiersProp = getPropertyOfType(modifiersType, propName);
10461+
const modifiersProp = isTypeUsableAsPropertyName(keyType) ? getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) : undefined;
1046210462
const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional ||
1046310463
!(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
1046410464
const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly ||

tests/baselines/reference/mappedTypeAsClauses.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ type DoubleProp<T> = { [P in keyof T & string as `${P}1` | `${P}2`]: T[P] }
2828
type TD1 = DoubleProp<{ a: string, b: number }>; // { a1: string, a2: string, b1: number, b2: number }
2929
type TD2 = keyof TD1; // 'a1' | 'a2' | 'b1' | 'b2'
3030
type TD3<U> = keyof DoubleProp<U>; // `${keyof U & string}1` | `${keyof U & string}2`
31+
32+
// Repro from #40619
33+
34+
type Lazyify<T> = {
35+
[K in keyof T as `get${capitalize string & K}`]: () => T[K]
36+
};
37+
38+
interface Person {
39+
readonly name: string;
40+
age: number;
41+
location?: string;
42+
}
43+
44+
type LazyPerson = Lazyify<Person>;
3145

3246

3347
//// [mappedTypeAsClauses.js]
@@ -82,3 +96,12 @@ declare type TD1 = DoubleProp<{
8296
}>;
8397
declare type TD2 = keyof TD1;
8498
declare type TD3<U> = keyof DoubleProp<U>;
99+
declare type Lazyify<T> = {
100+
[K in keyof T as `get${capitalize string & K}`]: () => T[K];
101+
};
102+
interface Person {
103+
readonly name: string;
104+
age: number;
105+
location?: string;
106+
}
107+
declare type LazyPerson = Lazyify<Person>;

tests/baselines/reference/mappedTypeAsClauses.symbols

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,36 @@ type TD3<U> = keyof DoubleProp<U>; // `${keyof U & string}1` | `${keyof U & str
108108
>DoubleProp : Symbol(DoubleProp, Decl(mappedTypeAsClauses.ts, 21, 85))
109109
>U : Symbol(U, Decl(mappedTypeAsClauses.ts, 28, 9))
110110

111+
// Repro from #40619
112+
113+
type Lazyify<T> = {
114+
>Lazyify : Symbol(Lazyify, Decl(mappedTypeAsClauses.ts, 28, 34))
115+
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 32, 13))
116+
117+
[K in keyof T as `get${capitalize string & K}`]: () => T[K]
118+
>K : Symbol(K, Decl(mappedTypeAsClauses.ts, 33, 5))
119+
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 32, 13))
120+
>K : Symbol(K, Decl(mappedTypeAsClauses.ts, 33, 5))
121+
>T : Symbol(T, Decl(mappedTypeAsClauses.ts, 32, 13))
122+
>K : Symbol(K, Decl(mappedTypeAsClauses.ts, 33, 5))
123+
124+
};
125+
126+
interface Person {
127+
>Person : Symbol(Person, Decl(mappedTypeAsClauses.ts, 34, 2))
128+
129+
readonly name: string;
130+
>name : Symbol(Person.name, Decl(mappedTypeAsClauses.ts, 36, 18))
131+
132+
age: number;
133+
>age : Symbol(Person.age, Decl(mappedTypeAsClauses.ts, 37, 26))
134+
135+
location?: string;
136+
>location : Symbol(Person.location, Decl(mappedTypeAsClauses.ts, 38, 16))
137+
}
138+
139+
type LazyPerson = Lazyify<Person>;
140+
>LazyPerson : Symbol(LazyPerson, Decl(mappedTypeAsClauses.ts, 40, 1))
141+
>Lazyify : Symbol(Lazyify, Decl(mappedTypeAsClauses.ts, 28, 34))
142+
>Person : Symbol(Person, Decl(mappedTypeAsClauses.ts, 34, 2))
143+

tests/baselines/reference/mappedTypeAsClauses.types

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,25 @@ type TD2 = keyof TD1; // 'a1' | 'a2' | 'b1' | 'b2'
6666
type TD3<U> = keyof DoubleProp<U>; // `${keyof U & string}1` | `${keyof U & string}2`
6767
>TD3 : `${keyof U & string}1` | `${keyof U & string}2`
6868

69+
// Repro from #40619
70+
71+
type Lazyify<T> = {
72+
>Lazyify : Lazyify<T>
73+
74+
[K in keyof T as `get${capitalize string & K}`]: () => T[K]
75+
};
76+
77+
interface Person {
78+
readonly name: string;
79+
>name : string
80+
81+
age: number;
82+
>age : number
83+
84+
location?: string;
85+
>location : string | undefined
86+
}
87+
88+
type LazyPerson = Lazyify<Person>;
89+
>LazyPerson : Lazyify<Person>
90+

tests/cases/conformance/types/mapped/mappedTypeAsClauses.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,17 @@ type DoubleProp<T> = { [P in keyof T & string as `${P}1` | `${P}2`]: T[P] }
3030
type TD1 = DoubleProp<{ a: string, b: number }>; // { a1: string, a2: string, b1: number, b2: number }
3131
type TD2 = keyof TD1; // 'a1' | 'a2' | 'b1' | 'b2'
3232
type TD3<U> = keyof DoubleProp<U>; // `${keyof U & string}1` | `${keyof U & string}2`
33+
34+
// Repro from #40619
35+
36+
type Lazyify<T> = {
37+
[K in keyof T as `get${capitalize string & K}`]: () => T[K]
38+
};
39+
40+
interface Person {
41+
readonly name: string;
42+
age: number;
43+
location?: string;
44+
}
45+
46+
type LazyPerson = Lazyify<Person>;

0 commit comments

Comments
 (0)