Skip to content

Commit 1dc207f

Browse files
committed
Merge remote-tracking branch 'upstream/master'
* upstream/master: Perform intersection reduction before and after getApparentType (microsoft#38565) fix(33836): allow readonly modifier for a field with only get accessor (microsoft#36543) Fix build type error (microsoft#38525)
2 parents 7367bf6 + c1f676d commit 1dc207f

10 files changed

+304
-28
lines changed

Diff for: src/compiler/checker.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -10205,7 +10205,7 @@ namespace ts {
1020510205
}
1020610206

1020710207
function getPropertiesOfType(type: Type): Symbol[] {
10208-
type = getApparentType(getReducedType(type));
10208+
type = getReducedApparentType(type);
1020910209
return type.flags & TypeFlags.UnionOrIntersection ?
1021010210
getPropertiesOfUnionOrIntersectionType(<UnionType>type) :
1021110211
getPropertiesOfObjectType(type);
@@ -10561,6 +10561,14 @@ namespace ts {
1056110561
t;
1056210562
}
1056310563

10564+
function getReducedApparentType(type: Type): Type {
10565+
// Since getApparentType may return a non-reduced union or intersection type, we need to perform
10566+
// type reduction both before and after obtaining the apparent type. For example, given a type parameter
10567+
// 'T extends A | B', the type 'T & X' becomes 'A & X | B & X' after obtaining the apparent type, and
10568+
// that type may need futher reduction to remove empty intersections.
10569+
return getReducedType(getApparentType(getReducedType(type)));
10570+
}
10571+
1056410572
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined {
1056510573
let singleProp: Symbol | undefined;
1056610574
let propSet: Map<Symbol> | undefined;
@@ -10782,7 +10790,7 @@ namespace ts {
1078210790
* @param name a name of property to look up in a given type
1078310791
*/
1078410792
function getPropertyOfType(type: Type, name: __String): Symbol | undefined {
10785-
type = getApparentType(getReducedType(type));
10793+
type = getReducedApparentType(type);
1078610794
if (type.flags & TypeFlags.Object) {
1078710795
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
1078810796
const symbol = resolved.members.get(name);
@@ -10820,7 +10828,7 @@ namespace ts {
1082010828
* maps primitive types and type parameters are to their apparent types.
1082110829
*/
1082210830
function getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[] {
10823-
return getSignaturesOfStructuredType(getApparentType(getReducedType(type)), kind);
10831+
return getSignaturesOfStructuredType(getReducedApparentType(type), kind);
1082410832
}
1082510833

1082610834
function getIndexInfoOfStructuredType(type: Type, kind: IndexKind): IndexInfo | undefined {
@@ -10838,13 +10846,13 @@ namespace ts {
1083810846
// Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and
1083910847
// maps primitive types and type parameters are to their apparent types.
1084010848
function getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined {
10841-
return getIndexInfoOfStructuredType(getApparentType(getReducedType(type)), kind);
10849+
return getIndexInfoOfStructuredType(getReducedApparentType(type), kind);
1084210850
}
1084310851

1084410852
// Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and
1084510853
// maps primitive types and type parameters are to their apparent types.
1084610854
function getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined {
10847-
return getIndexTypeOfStructuredType(getApparentType(getReducedType(type)), kind);
10855+
return getIndexTypeOfStructuredType(getReducedApparentType(type), kind);
1084810856
}
1084910857

1085010858
function getImplicitIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined {
@@ -13218,7 +13226,7 @@ namespace ts {
1321813226
// In the following we resolve T[K] to the type of the property in T selected by K.
1321913227
// We treat boolean as different from other unions to improve errors;
1322013228
// skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'.
13221-
const apparentObjectType = getApparentType(getReducedType(objectType));
13229+
const apparentObjectType = getReducedApparentType(objectType);
1322213230
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Boolean)) {
1322313231
const propTypes: Type[] = [];
1322413232
let wasMissingProp = false;

Diff for: src/harness/vfsUtil.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -649,14 +649,14 @@ namespace vfs {
649649
*
650650
* NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
651651
*/
652-
public readFileSync(path: string, encoding: string): string;
652+
public readFileSync(path: string, encoding: BufferEncoding): string;
653653
/**
654654
* Read from a file.
655655
*
656656
* NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
657657
*/
658-
public readFileSync(path: string, encoding?: string | null): string | Buffer;
659-
public readFileSync(path: string, encoding: string | null = null) { // eslint-disable-line no-null/no-null
658+
public readFileSync(path: string, encoding?: BufferEncoding | null): string | Buffer;
659+
public readFileSync(path: string, encoding: BufferEncoding | null = null) { // eslint-disable-line no-null/no-null
660660
const { node } = this._walk(this._resolve(path));
661661
if (!node) throw createIOError("ENOENT");
662662
if (isDirectory(node)) throw createIOError("EISDIR");

Diff for: src/services/refactors/generateGetAccessorAndSetAccessor.ts

+34-16
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
4141
const fieldInfo = getConvertibleFieldAtPosition(context);
4242
if (!fieldInfo) return undefined;
4343

44-
const isJS = isSourceFileJS(file);
4544
const changeTracker = textChanges.ChangeTracker.fromContext(context);
4645
const { isStatic, isReadonly, fieldName, accessorName, originalName, type, container, declaration, renameAccessor } = fieldInfo;
4746

@@ -50,15 +49,20 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
5049
suppressLeadingAndTrailingTrivia(declaration);
5150
suppressLeadingAndTrailingTrivia(container);
5251

53-
const isInClassLike = isClassLike(container);
54-
// avoid Readonly modifier because it will convert to get accessor
55-
const modifierFlags = getEffectiveModifierFlags(declaration) & ~ModifierFlags.Readonly;
56-
const accessorModifiers = isInClassLike
57-
? !modifierFlags || modifierFlags & ModifierFlags.Private
58-
? getModifiers(isJS, isStatic, SyntaxKind.PublicKeyword)
59-
: createNodeArray(createModifiersFromModifierFlags(modifierFlags))
60-
: undefined;
61-
const fieldModifiers = isInClassLike ? getModifiers(isJS, isStatic, SyntaxKind.PrivateKeyword) : undefined;
52+
let accessorModifiers: ModifiersArray | undefined;
53+
let fieldModifiers: ModifiersArray | undefined;
54+
if (isClassLike(container)) {
55+
const modifierFlags = getEffectiveModifierFlags(declaration);
56+
if (isSourceFileJS(file)) {
57+
const modifiers = createModifiers(modifierFlags);
58+
accessorModifiers = modifiers;
59+
fieldModifiers = modifiers;
60+
}
61+
else {
62+
accessorModifiers = createModifiers(prepareModifierFlagsForAccessor(modifierFlags));
63+
fieldModifiers = createModifiers(prepareModifierFlagsForField(modifierFlags));
64+
}
65+
}
6266

6367
updateFieldDeclaration(changeTracker, file, declaration, fieldName, fieldModifiers);
6468

@@ -105,12 +109,26 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor {
105109
return isIdentifier(fieldName) ? createPropertyAccess(leftHead, fieldName) : createElementAccess(leftHead, createLiteral(fieldName));
106110
}
107111

108-
function getModifiers(isJS: boolean, isStatic: boolean, accessModifier: SyntaxKind.PublicKeyword | SyntaxKind.PrivateKeyword): NodeArray<Modifier> | undefined {
109-
const modifiers = append<Modifier>(
110-
!isJS ? [createToken(accessModifier) as Token<SyntaxKind.PublicKeyword> | Token<SyntaxKind.PrivateKeyword>] : undefined,
111-
isStatic ? createToken(SyntaxKind.StaticKeyword) : undefined
112-
);
113-
return modifiers && createNodeArray(modifiers);
112+
function createModifiers(modifierFlags: ModifierFlags): ModifiersArray | undefined {
113+
return modifierFlags ? createNodeArray(createModifiersFromModifierFlags(modifierFlags)) : undefined;
114+
}
115+
116+
function prepareModifierFlagsForAccessor(modifierFlags: ModifierFlags): ModifierFlags {
117+
modifierFlags &= ~ModifierFlags.Readonly; // avoid Readonly modifier because it will convert to get accessor
118+
modifierFlags &= ~ModifierFlags.Private;
119+
120+
if (!(modifierFlags & ModifierFlags.Protected)) {
121+
modifierFlags |= ModifierFlags.Public;
122+
}
123+
124+
return modifierFlags;
125+
}
126+
127+
function prepareModifierFlagsForField(modifierFlags: ModifierFlags): ModifierFlags {
128+
modifierFlags &= ~ModifierFlags.Public;
129+
modifierFlags &= ~ModifierFlags.Protected;
130+
modifierFlags |= ModifierFlags.Private;
131+
return modifierFlags;
114132
}
115133

116134
function getConvertibleFieldAtPosition(context: RefactorContext): Info | undefined {

Diff for: tests/baselines/reference/intersectionReduction.errors.txt

+32
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,36 @@ tests/cases/conformance/types/intersection/intersectionReduction.ts(81,1): error
120120
const f2 = (t: Container<"a"> | (Container<"b"> & Container<"c">)): Container<"a"> => t;
121121
const f3 = (t: Container<"a"> | (Container<"b"> & { dataB: boolean } & Container<"a">)): Container<"a"> => t;
122122
const f4 = (t: number | (Container<"b"> & { dataB: boolean } & Container<"a">)): number => t;
123+
124+
// Repro from #38549
125+
126+
interface A2 {
127+
kind: "A";
128+
a: number;
129+
}
130+
131+
interface B2 {
132+
kind: "B";
133+
b: number;
134+
}
135+
136+
declare const shouldBeB: (A2 | B2) & B2;
137+
const b: B2 = shouldBeB; // works
138+
139+
function inGeneric<T extends A2 | B2>(alsoShouldBeB: T & B2) {
140+
const b: B2 = alsoShouldBeB;
141+
}
142+
143+
// Repro from #38542
144+
145+
interface ABI {
146+
kind: 'a' | 'b';
147+
}
148+
149+
declare class CA { kind: 'a'; a: string; x: number };
150+
declare class CB { kind: 'b'; b: string; y: number };
151+
152+
function bar<T extends CA | CB>(x: T & CA) {
153+
let ab: ABI = x;
154+
}
123155

Diff for: tests/baselines/reference/intersectionReduction.js

+41
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,38 @@ type Container<Type extends string> = {
107107
const f2 = (t: Container<"a"> | (Container<"b"> & Container<"c">)): Container<"a"> => t;
108108
const f3 = (t: Container<"a"> | (Container<"b"> & { dataB: boolean } & Container<"a">)): Container<"a"> => t;
109109
const f4 = (t: number | (Container<"b"> & { dataB: boolean } & Container<"a">)): number => t;
110+
111+
// Repro from #38549
112+
113+
interface A2 {
114+
kind: "A";
115+
a: number;
116+
}
117+
118+
interface B2 {
119+
kind: "B";
120+
b: number;
121+
}
122+
123+
declare const shouldBeB: (A2 | B2) & B2;
124+
const b: B2 = shouldBeB; // works
125+
126+
function inGeneric<T extends A2 | B2>(alsoShouldBeB: T & B2) {
127+
const b: B2 = alsoShouldBeB;
128+
}
129+
130+
// Repro from #38542
131+
132+
interface ABI {
133+
kind: 'a' | 'b';
134+
}
135+
136+
declare class CA { kind: 'a'; a: string; x: number };
137+
declare class CB { kind: 'b'; b: string; y: number };
138+
139+
function bar<T extends CA | CB>(x: T & CA) {
140+
let ab: ABI = x;
141+
}
110142

111143

112144
//// [intersectionReduction.js]
@@ -128,3 +160,12 @@ var f1 = function (t) { return t; };
128160
var f2 = function (t) { return t; };
129161
var f3 = function (t) { return t; };
130162
var f4 = function (t) { return t; };
163+
var b = shouldBeB; // works
164+
function inGeneric(alsoShouldBeB) {
165+
var b = alsoShouldBeB;
166+
}
167+
;
168+
;
169+
function bar(x) {
170+
var ab = x;
171+
}

Diff for: tests/baselines/reference/intersectionReduction.symbols

+84
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,87 @@ const f4 = (t: number | (Container<"b"> & { dataB: boolean } & Container<"a">)):
373373
>Container : Symbol(Container, Decl(intersectionReduction.ts, 99, 44))
374374
>t : Symbol(t, Decl(intersectionReduction.ts, 107, 12))
375375

376+
// Repro from #38549
377+
378+
interface A2 {
379+
>A2 : Symbol(A2, Decl(intersectionReduction.ts, 107, 93))
380+
381+
kind: "A";
382+
>kind : Symbol(A2.kind, Decl(intersectionReduction.ts, 111, 14))
383+
384+
a: number;
385+
>a : Symbol(A2.a, Decl(intersectionReduction.ts, 112, 14))
386+
}
387+
388+
interface B2 {
389+
>B2 : Symbol(B2, Decl(intersectionReduction.ts, 114, 1))
390+
391+
kind: "B";
392+
>kind : Symbol(B2.kind, Decl(intersectionReduction.ts, 116, 14))
393+
394+
b: number;
395+
>b : Symbol(B2.b, Decl(intersectionReduction.ts, 117, 14))
396+
}
397+
398+
declare const shouldBeB: (A2 | B2) & B2;
399+
>shouldBeB : Symbol(shouldBeB, Decl(intersectionReduction.ts, 121, 13))
400+
>A2 : Symbol(A2, Decl(intersectionReduction.ts, 107, 93))
401+
>B2 : Symbol(B2, Decl(intersectionReduction.ts, 114, 1))
402+
>B2 : Symbol(B2, Decl(intersectionReduction.ts, 114, 1))
403+
404+
const b: B2 = shouldBeB; // works
405+
>b : Symbol(b, Decl(intersectionReduction.ts, 122, 5))
406+
>B2 : Symbol(B2, Decl(intersectionReduction.ts, 114, 1))
407+
>shouldBeB : Symbol(shouldBeB, Decl(intersectionReduction.ts, 121, 13))
408+
409+
function inGeneric<T extends A2 | B2>(alsoShouldBeB: T & B2) {
410+
>inGeneric : Symbol(inGeneric, Decl(intersectionReduction.ts, 122, 24))
411+
>T : Symbol(T, Decl(intersectionReduction.ts, 124, 19))
412+
>A2 : Symbol(A2, Decl(intersectionReduction.ts, 107, 93))
413+
>B2 : Symbol(B2, Decl(intersectionReduction.ts, 114, 1))
414+
>alsoShouldBeB : Symbol(alsoShouldBeB, Decl(intersectionReduction.ts, 124, 38))
415+
>T : Symbol(T, Decl(intersectionReduction.ts, 124, 19))
416+
>B2 : Symbol(B2, Decl(intersectionReduction.ts, 114, 1))
417+
418+
const b: B2 = alsoShouldBeB;
419+
>b : Symbol(b, Decl(intersectionReduction.ts, 125, 9))
420+
>B2 : Symbol(B2, Decl(intersectionReduction.ts, 114, 1))
421+
>alsoShouldBeB : Symbol(alsoShouldBeB, Decl(intersectionReduction.ts, 124, 38))
422+
}
423+
424+
// Repro from #38542
425+
426+
interface ABI {
427+
>ABI : Symbol(ABI, Decl(intersectionReduction.ts, 126, 1))
428+
429+
kind: 'a' | 'b';
430+
>kind : Symbol(ABI.kind, Decl(intersectionReduction.ts, 130, 15))
431+
}
432+
433+
declare class CA { kind: 'a'; a: string; x: number };
434+
>CA : Symbol(CA, Decl(intersectionReduction.ts, 132, 1))
435+
>kind : Symbol(CA.kind, Decl(intersectionReduction.ts, 134, 18))
436+
>a : Symbol(CA.a, Decl(intersectionReduction.ts, 134, 29))
437+
>x : Symbol(CA.x, Decl(intersectionReduction.ts, 134, 40))
438+
439+
declare class CB { kind: 'b'; b: string; y: number };
440+
>CB : Symbol(CB, Decl(intersectionReduction.ts, 134, 53))
441+
>kind : Symbol(CB.kind, Decl(intersectionReduction.ts, 135, 18))
442+
>b : Symbol(CB.b, Decl(intersectionReduction.ts, 135, 29))
443+
>y : Symbol(CB.y, Decl(intersectionReduction.ts, 135, 40))
444+
445+
function bar<T extends CA | CB>(x: T & CA) {
446+
>bar : Symbol(bar, Decl(intersectionReduction.ts, 135, 53))
447+
>T : Symbol(T, Decl(intersectionReduction.ts, 137, 13))
448+
>CA : Symbol(CA, Decl(intersectionReduction.ts, 132, 1))
449+
>CB : Symbol(CB, Decl(intersectionReduction.ts, 134, 53))
450+
>x : Symbol(x, Decl(intersectionReduction.ts, 137, 32))
451+
>T : Symbol(T, Decl(intersectionReduction.ts, 137, 13))
452+
>CA : Symbol(CA, Decl(intersectionReduction.ts, 132, 1))
453+
454+
let ab: ABI = x;
455+
>ab : Symbol(ab, Decl(intersectionReduction.ts, 138, 7))
456+
>ABI : Symbol(ABI, Decl(intersectionReduction.ts, 126, 1))
457+
>x : Symbol(x, Decl(intersectionReduction.ts, 137, 32))
458+
}
459+

Diff for: tests/baselines/reference/intersectionReduction.types

+62
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,65 @@ const f4 = (t: number | (Container<"b"> & { dataB: boolean } & Container<"a">)):
315315
>dataB : boolean
316316
>t : number
317317

318+
// Repro from #38549
319+
320+
interface A2 {
321+
kind: "A";
322+
>kind : "A"
323+
324+
a: number;
325+
>a : number
326+
}
327+
328+
interface B2 {
329+
kind: "B";
330+
>kind : "B"
331+
332+
b: number;
333+
>b : number
334+
}
335+
336+
declare const shouldBeB: (A2 | B2) & B2;
337+
>shouldBeB : B2
338+
339+
const b: B2 = shouldBeB; // works
340+
>b : B2
341+
>shouldBeB : B2
342+
343+
function inGeneric<T extends A2 | B2>(alsoShouldBeB: T & B2) {
344+
>inGeneric : <T extends A2 | B2>(alsoShouldBeB: T & B2) => void
345+
>alsoShouldBeB : T & B2
346+
347+
const b: B2 = alsoShouldBeB;
348+
>b : B2
349+
>alsoShouldBeB : T & B2
350+
}
351+
352+
// Repro from #38542
353+
354+
interface ABI {
355+
kind: 'a' | 'b';
356+
>kind : "a" | "b"
357+
}
358+
359+
declare class CA { kind: 'a'; a: string; x: number };
360+
>CA : CA
361+
>kind : "a"
362+
>a : string
363+
>x : number
364+
365+
declare class CB { kind: 'b'; b: string; y: number };
366+
>CB : CB
367+
>kind : "b"
368+
>b : string
369+
>y : number
370+
371+
function bar<T extends CA | CB>(x: T & CA) {
372+
>bar : <T extends CA | CB>(x: T & CA) => void
373+
>x : T & CA
374+
375+
let ab: ABI = x;
376+
>ab : ABI
377+
>x : T & CA
378+
}
379+

0 commit comments

Comments
 (0)