Skip to content

Commit 6ea1038

Browse files
authored
Merge pull request #12567 from Microsoft/mergeMaster1129
Merge master 1129
2 parents f0ce14b + d81448c commit 6ea1038

20 files changed

+1204
-90
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4497,12 +4497,14 @@ namespace ts {
44974497
// Resolve upfront such that recursive references see an empty object type.
44984498
setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
44994499
// In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
4500-
// and T as the template type.
4500+
// and T as the template type. If K is of the form 'keyof S', the mapped type and S are
4501+
// isomorphic and we copy property modifiers from corresponding properties in S.
45014502
const typeParameter = getTypeParameterFromMappedType(type);
45024503
const constraintType = getConstraintTypeFromMappedType(type);
4504+
const isomorphicType = getIsomorphicTypeFromMappedType(type);
45034505
const templateType = getTemplateTypeFromMappedType(type);
4504-
const isReadonly = !!type.declaration.readonlyToken;
4505-
const isOptional = !!type.declaration.questionToken;
4506+
const templateReadonly = !!type.declaration.readonlyToken;
4507+
const templateOptional = !!type.declaration.questionToken;
45064508
// First, if the constraint type is a type parameter, obtain the base constraint. Then,
45074509
// if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
45084510
// Finally, iterate over the constituents of the resulting iteration type.
@@ -4515,18 +4517,19 @@ namespace ts {
45154517
const iterationMapper = createUnaryTypeMapper(typeParameter, t);
45164518
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
45174519
const propType = instantiateType(templateType, templateMapper);
4518-
// If the current iteration type constituent is a literal type, create a property.
4519-
// Otherwise, for type string create a string index signature and for type number
4520-
// create a numeric index signature.
4521-
if (t.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
4520+
// If the current iteration type constituent is a string literal type, create a property.
4521+
// Otherwise, for type string create a string index signature.
4522+
if (t.flags & TypeFlags.StringLiteral) {
45224523
const propName = (<LiteralType>t).text;
4524+
const isomorphicProp = isomorphicType && getPropertyOfType(isomorphicType, propName);
4525+
const isOptional = templateOptional || !!(isomorphicProp && isomorphicProp.flags & SymbolFlags.Optional);
45234526
const prop = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName);
45244527
prop.type = addOptionality(propType, isOptional);
4525-
prop.isReadonly = isReadonly;
4528+
prop.isReadonly = templateReadonly || isomorphicProp && isReadonlySymbol(isomorphicProp);
45264529
members[propName] = prop;
45274530
}
45284531
else if (t.flags & TypeFlags.String) {
4529-
stringIndexInfo = createIndexInfo(propType, isReadonly);
4532+
stringIndexInfo = createIndexInfo(propType, templateReadonly);
45304533
}
45314534
});
45324535
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
@@ -4549,6 +4552,11 @@ namespace ts {
45494552
unknownType);
45504553
}
45514554

4555+
function getIsomorphicTypeFromMappedType(type: MappedType) {
4556+
const constraint = getConstraintDeclaration(getTypeParameterFromMappedType(type));
4557+
return constraint.kind === SyntaxKind.TypeOperator ? instantiateType(getTypeFromTypeNode((<TypeOperatorNode>constraint).type), type.mapper || identityMapper) : undefined;
4558+
}
4559+
45524560
function getErasedTemplateTypeFromMappedType(type: MappedType) {
45534561
return instantiateType(getTemplateTypeFromMappedType(type), createUnaryTypeMapper(getTypeParameterFromMappedType(type), anyType));
45544562
}
@@ -21239,6 +21247,9 @@ namespace ts {
2123921247
else if (accessor.body === undefined && !(getModifierFlags(accessor) & ModifierFlags.Abstract)) {
2124021248
return grammarErrorAtPos(getSourceFileOfNode(accessor), accessor.end - 1, ";".length, Diagnostics._0_expected, "{");
2124121249
}
21250+
else if (accessor.body && getModifierFlags(accessor) & ModifierFlags.Abstract) {
21251+
return grammarErrorOnNode(accessor, Diagnostics.An_abstract_accessor_cannot_have_an_implementation);
21252+
}
2124221253
else if (accessor.typeParameters) {
2124321254
return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters);
2124421255
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,10 @@
851851
"category": "Error",
852852
"code": 1317
853853
},
854+
"An abstract accessor cannot have an implementation.": {
855+
"category": "Error",
856+
"code": 1318
857+
},
854858
"Duplicate identifier '{0}'.": {
855859
"category": "Error",
856860
"code": 2300

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,8 @@ namespace ts.projectSystem {
5151
throttleLimit: number,
5252
installTypingHost: server.ServerHost,
5353
readonly typesRegistry = createMap<void>(),
54-
telemetryEnabled?: boolean,
5554
log?: TI.Log) {
56-
super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, telemetryEnabled, log);
55+
super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, log);
5756
}
5857

5958
safeFileList = safeList.path;

src/harness/unittests/typingsInstaller.ts

Lines changed: 107 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ namespace ts.projectSystem {
2020
}
2121

2222
class Installer extends TestTypingsInstaller {
23-
constructor(host: server.ServerHost, p?: InstallerParams, telemetryEnabled?: boolean, log?: TI.Log) {
23+
constructor(host: server.ServerHost, p?: InstallerParams, log?: TI.Log) {
2424
super(
2525
(p && p.globalTypingsCacheLocation) || "/a/data",
2626
(p && p.throttleLimit) || 5,
2727
host,
2828
(p && p.typesRegistry),
29-
telemetryEnabled,
3029
log);
3130
}
3231

@@ -36,7 +35,7 @@ namespace ts.projectSystem {
3635
}
3736
}
3837

39-
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
38+
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[] | string, typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
4039
self.addPostExecAction(installedTypings, success => {
4140
for (const file of typingFiles) {
4241
host.createFileOrFolder(file, /*createParentDirectory*/ true);
@@ -907,7 +906,7 @@ namespace ts.projectSystem {
907906
const host = createServerHost([f1, packageJson]);
908907
const installer = new (class extends Installer {
909908
constructor() {
910-
super(host, { globalTypingsCacheLocation: "/tmp" }, /*telemetryEnabled*/ false, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
909+
super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
911910
}
912911
installWorker(_requestId: number, _args: string[], _cwd: string, _cb: server.typingsInstaller.RequestCompletedAction) {
913912
assert(false, "runCommand should not be invoked");
@@ -971,15 +970,18 @@ namespace ts.projectSystem {
971970
let seenTelemetryEvent = false;
972971
const installer = new (class extends Installer {
973972
constructor() {
974-
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") }, /*telemetryEnabled*/ true);
973+
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
975974
}
976975
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
977976
const installedTypings = ["@types/commander"];
978977
const typingFiles = [commander];
979978
executeCommand(this, host, installedTypings, typingFiles, cb);
980979
}
981-
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.TypingsInstallEvent) {
982-
if (response.kind === server.EventInstall) {
980+
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
981+
if (response.kind === server.EventBeginInstallTypes) {
982+
return;
983+
}
984+
if (response.kind === server.EventEndInstallTypes) {
983985
assert.deepEqual(response.packagesToInstall, ["@types/commander"]);
984986
seenTelemetryEvent = true;
985987
return;
@@ -997,4 +999,102 @@ namespace ts.projectSystem {
997999
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
9981000
});
9991001
});
1002+
1003+
describe("progress notifications", () => {
1004+
it ("should be sent for success", () => {
1005+
const f1 = {
1006+
path: "/a/app.js",
1007+
content: ""
1008+
};
1009+
const package = {
1010+
path: "/a/package.json",
1011+
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
1012+
};
1013+
const cachePath = "/a/cache/";
1014+
const commander = {
1015+
path: cachePath + "node_modules/@types/commander/index.d.ts",
1016+
content: "export let x: number"
1017+
};
1018+
const host = createServerHost([f1, package]);
1019+
let beginEvent: server.BeginInstallTypes;
1020+
let endEvent: server.EndInstallTypes;
1021+
const installer = new (class extends Installer {
1022+
constructor() {
1023+
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
1024+
}
1025+
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
1026+
const installedTypings = ["@types/commander"];
1027+
const typingFiles = [commander];
1028+
executeCommand(this, host, installedTypings, typingFiles, cb);
1029+
}
1030+
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
1031+
if (response.kind === server.EventBeginInstallTypes) {
1032+
beginEvent = response;
1033+
return;
1034+
}
1035+
if (response.kind === server.EventEndInstallTypes) {
1036+
endEvent = response;
1037+
return;
1038+
}
1039+
super.sendResponse(response);
1040+
}
1041+
})();
1042+
const projectService = createProjectService(host, { typingsInstaller: installer });
1043+
projectService.openClientFile(f1.path);
1044+
1045+
installer.installAll(/*expectedCount*/ 1);
1046+
1047+
assert.isTrue(!!beginEvent);
1048+
assert.isTrue(!!endEvent);
1049+
assert.isTrue(beginEvent.eventId === endEvent.eventId);
1050+
assert.isTrue(endEvent.installSuccess);
1051+
checkNumberOfProjects(projectService, { inferredProjects: 1 });
1052+
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
1053+
});
1054+
1055+
it ("should be sent for error", () => {
1056+
const f1 = {
1057+
path: "/a/app.js",
1058+
content: ""
1059+
};
1060+
const package = {
1061+
path: "/a/package.json",
1062+
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
1063+
};
1064+
const cachePath = "/a/cache/";
1065+
const host = createServerHost([f1, package]);
1066+
let beginEvent: server.BeginInstallTypes;
1067+
let endEvent: server.EndInstallTypes;
1068+
const installer = new (class extends Installer {
1069+
constructor() {
1070+
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
1071+
}
1072+
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
1073+
executeCommand(this, host, "", [], cb);
1074+
}
1075+
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
1076+
if (response.kind === server.EventBeginInstallTypes) {
1077+
beginEvent = response;
1078+
return;
1079+
}
1080+
if (response.kind === server.EventEndInstallTypes) {
1081+
endEvent = response;
1082+
return;
1083+
}
1084+
super.sendResponse(response);
1085+
}
1086+
})();
1087+
const projectService = createProjectService(host, { typingsInstaller: installer });
1088+
projectService.openClientFile(f1.path);
1089+
1090+
installer.installAll(/*expectedCount*/ 1);
1091+
1092+
assert.isTrue(!!beginEvent);
1093+
assert.isTrue(!!endEvent);
1094+
assert.isTrue(beginEvent.eventId === endEvent.eventId);
1095+
assert.isFalse(endEvent.installSuccess);
1096+
checkNumberOfProjects(projectService, { inferredProjects: 1 });
1097+
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path]);
1098+
});
1099+
});
10001100
}

src/lib/es2017.object.d.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,22 @@ interface ObjectConstructor {
44
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
55
*/
66
values<T>(o: { [s: string]: T }): T[];
7+
8+
/**
9+
* Returns an array of values of the enumerable properties of an object
10+
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
11+
*/
712
values(o: any): any[];
13+
14+
/**
15+
* Returns an array of key/values of the enumerable properties of an object
16+
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
17+
*/
18+
entries<T>(o: { [s: string]: T }): [string, T][];
19+
820
/**
921
* Returns an array of key/values of the enumerable properties of an object
1022
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
1123
*/
12-
entries<T extends { [key: string]: any }, K extends keyof T>(o: T): [keyof T, T[K]][];
1324
entries(o: any): [string, any][];
1425
}

src/server/protocol.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,40 @@ namespace ts.server.protocol {
21172117
typingsInstallerVersion: string;
21182118
}
21192119

2120+
export type BeginInstallTypesEventName = "beginInstallTypes";
2121+
export type EndInstallTypesEventName = "endInstallTypes";
2122+
2123+
export interface BeginInstallTypesEvent extends Event {
2124+
event: BeginInstallTypesEventName;
2125+
body: BeginInstallTypesEventBody;
2126+
}
2127+
2128+
export interface EndInstallTypesEvent extends Event {
2129+
event: EndInstallTypesEventName;
2130+
body: EndInstallTypesEventBody;
2131+
}
2132+
2133+
export interface InstallTypesEventBody {
2134+
/**
2135+
* correlation id to match begin and end events
2136+
*/
2137+
eventId: number;
2138+
/**
2139+
* list of packages to install
2140+
*/
2141+
packages: ReadonlyArray<string>;
2142+
}
2143+
2144+
export interface BeginInstallTypesEventBody extends InstallTypesEventBody {
2145+
}
2146+
2147+
export interface EndInstallTypesEventBody extends InstallTypesEventBody {
2148+
/**
2149+
* true if installation succeeded, otherwise false
2150+
*/
2151+
success: boolean;
2152+
}
2153+
21202154
export interface NavBarResponse extends Response {
21212155
body?: NavigationBarItem[];
21222156
}

src/server/server.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ namespace ts.server {
198198
private socket: NodeSocket;
199199
private projectService: ProjectService;
200200
private throttledOperations: ThrottledOperations;
201-
private telemetrySender: EventSender;
201+
private eventSender: EventSender;
202202

203203
constructor(
204204
private readonly telemetryEnabled: boolean,
@@ -231,7 +231,7 @@ namespace ts.server {
231231
}
232232

233233
setTelemetrySender(telemetrySender: EventSender) {
234-
this.telemetrySender = telemetrySender;
234+
this.eventSender = telemetrySender;
235235
}
236236

237237
attach(projectService: ProjectService) {
@@ -291,12 +291,30 @@ namespace ts.server {
291291
});
292292
}
293293

294-
private handleMessage(response: SetTypings | InvalidateCachedTypings | TypingsInstallEvent) {
294+
private handleMessage(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes) {
295295
if (this.logger.hasLevel(LogLevel.verbose)) {
296296
this.logger.info(`Received response: ${JSON.stringify(response)}`);
297297
}
298-
if (response.kind === EventInstall) {
299-
if (this.telemetrySender) {
298+
299+
if (response.kind === EventBeginInstallTypes) {
300+
if (!this.eventSender) {
301+
return;
302+
}
303+
const body: protocol.BeginInstallTypesEventBody = {
304+
eventId: response.eventId,
305+
packages: response.packagesToInstall,
306+
};
307+
const eventName: protocol.BeginInstallTypesEventName = "beginInstallTypes";
308+
this.eventSender.event(body, eventName);
309+
310+
return;
311+
}
312+
313+
if (response.kind === EventEndInstallTypes) {
314+
if (!this.eventSender) {
315+
return;
316+
}
317+
if (this.telemetryEnabled) {
300318
const body: protocol.TypingsInstalledTelemetryEventBody = {
301319
telemetryEventName: "typingsInstalled",
302320
payload: {
@@ -306,10 +324,19 @@ namespace ts.server {
306324
}
307325
};
308326
const eventName: protocol.TelemetryEventName = "telemetry";
309-
this.telemetrySender.event(body, eventName);
327+
this.eventSender.event(body, eventName);
310328
}
329+
330+
const body: protocol.EndInstallTypesEventBody = {
331+
eventId: response.eventId,
332+
packages: response.packagesToInstall,
333+
success: response.installSuccess,
334+
};
335+
const eventName: protocol.EndInstallTypesEventName = "endInstallTypes";
336+
this.eventSender.event(body, eventName);
311337
return;
312338
}
339+
313340
this.projectService.updateTypingsForProject(response);
314341
if (response.kind == ActionSet && this.socket) {
315342
this.sendEvent(0, "setTypings", response);

0 commit comments

Comments
 (0)