Skip to content

Commit 75117ac

Browse files
authored
Merge pull request #33598 from microsoft/fix33582
Fix issues caused by reachability analysis of `this.foo(...)` like calls.
2 parents 9faca24 + 0d4e594 commit 75117ac

File tree

6 files changed

+671
-26
lines changed

6 files changed

+671
-26
lines changed

Diff for: src/compiler/checker.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -17135,7 +17135,7 @@ namespace ts {
1713517135
const symbol = getResolvedSymbol(<Identifier>node);
1713617136
return getExplicitTypeOfSymbol(symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
1713717137
case SyntaxKind.ThisKeyword:
17138-
return checkThisExpression(node);
17138+
return getExplicitThisType(node);
1713917139
case SyntaxKind.PropertyAccessExpression:
1714017140
const type = getTypeOfDottedName((<PropertyAccessExpression>node).expression);
1714117141
const prop = type && getPropertyOfType(type, (<PropertyAccessExpression>node).name.escapedText);
@@ -18720,6 +18720,20 @@ namespace ts {
1872018720
}
1872118721
}
1872218722

18723+
function getExplicitThisType(node: Expression) {
18724+
const container = getThisContainer(node, /*includeArrowFunctions*/ false);
18725+
if (isFunctionLike(container)) {
18726+
const signature = getSignatureFromDeclaration(container);
18727+
if (signature.thisParameter) {
18728+
return getExplicitTypeOfSymbol(signature.thisParameter);
18729+
}
18730+
}
18731+
if (isClassLike(container.parent)) {
18732+
const symbol = getSymbolOfNode(container.parent);
18733+
return hasModifier(container, ModifierFlags.Static) ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol) as InterfaceType).thisType!;
18734+
}
18735+
}
18736+
1872318737
function getClassNameFromPrototypeMethod(container: Node) {
1872418738
// Check if it's the RHS of a x.prototype.y = function [name]() { .... }
1872518739
if (container.kind === SyntaxKind.FunctionExpression &&

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

+69
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,73 @@ tests/cases/conformance/controlFlow/neverReturningFunctions1.ts(153,5): error TS
224224
~~
225225
!!! error TS7027: Unreachable code detected.
226226
}
227+
228+
// Repro from #33582
229+
230+
export interface Component<T extends object = any> {
231+
attrName?: string;
232+
data: T;
233+
dependencies?: string[];
234+
el: any;
235+
id: string;
236+
multiple?: boolean;
237+
name: string;
238+
schema: unknown;
239+
system: any;
240+
241+
init(data?: T): void;
242+
pause(): void;
243+
play(): void;
244+
remove(): void;
245+
tick?(time: number, timeDelta: number): void;
246+
update(oldData: T): void;
247+
updateSchema?(): void;
248+
249+
extendSchema(update: unknown): void;
250+
flushToDOM(): void;
251+
}
252+
253+
export interface ComponentConstructor<T extends object> {
254+
new (el: unknown, attrValue: string, id: string): T & Component;
255+
prototype: T & {
256+
name: string;
257+
system: unknown;
258+
play(): void;
259+
pause(): void;
260+
};
261+
}
262+
263+
declare function registerComponent<T extends object>(
264+
name: string,
265+
component: ComponentDefinition<T>
266+
): ComponentConstructor<T>;
267+
268+
export type ComponentDefinition<T extends object = object> = T & Partial<Component> & ThisType<T & Component>;
269+
270+
const Component = registerComponent('test-component', {
271+
schema: {
272+
myProperty: {
273+
default: [],
274+
parse() {
275+
return [true];
276+
}
277+
},
278+
string: { type: 'string' },
279+
num: 0
280+
},
281+
init() {
282+
this.data.num = 0;
283+
this.el.setAttribute('custom-attribute', 'custom-value');
284+
},
285+
update() {},
286+
tick() {},
287+
remove() {},
288+
pause() {},
289+
play() {},
290+
291+
multiply(f: number) {
292+
// Reference to system because both were registered with the same name.
293+
return f * this.data.num * this.system!.data.counter;
294+
}
295+
});
227296

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

+123-25
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,80 @@ function f42(x: number) {
153153
}
154154
x; // Unreachable
155155
}
156+
157+
// Repro from #33582
158+
159+
export interface Component<T extends object = any> {
160+
attrName?: string;
161+
data: T;
162+
dependencies?: string[];
163+
el: any;
164+
id: string;
165+
multiple?: boolean;
166+
name: string;
167+
schema: unknown;
168+
system: any;
169+
170+
init(data?: T): void;
171+
pause(): void;
172+
play(): void;
173+
remove(): void;
174+
tick?(time: number, timeDelta: number): void;
175+
update(oldData: T): void;
176+
updateSchema?(): void;
177+
178+
extendSchema(update: unknown): void;
179+
flushToDOM(): void;
180+
}
181+
182+
export interface ComponentConstructor<T extends object> {
183+
new (el: unknown, attrValue: string, id: string): T & Component;
184+
prototype: T & {
185+
name: string;
186+
system: unknown;
187+
play(): void;
188+
pause(): void;
189+
};
190+
}
191+
192+
declare function registerComponent<T extends object>(
193+
name: string,
194+
component: ComponentDefinition<T>
195+
): ComponentConstructor<T>;
196+
197+
export type ComponentDefinition<T extends object = object> = T & Partial<Component> & ThisType<T & Component>;
198+
199+
const Component = registerComponent('test-component', {
200+
schema: {
201+
myProperty: {
202+
default: [],
203+
parse() {
204+
return [true];
205+
}
206+
},
207+
string: { type: 'string' },
208+
num: 0
209+
},
210+
init() {
211+
this.data.num = 0;
212+
this.el.setAttribute('custom-attribute', 'custom-value');
213+
},
214+
update() {},
215+
tick() {},
216+
remove() {},
217+
pause() {},
218+
play() {},
219+
220+
multiply(f: number) {
221+
// Reference to system because both were registered with the same name.
222+
return f * this.data.num * this.system!.data.counter;
223+
}
224+
});
156225

157226

158227
//// [neverReturningFunctions1.js]
159228
"use strict";
229+
exports.__esModule = true;
160230
function fail(message) {
161231
throw new Error(message);
162232
}
@@ -305,33 +375,61 @@ function f42(x) {
305375
}
306376
x; // Unreachable
307377
}
378+
var Component = registerComponent('test-component', {
379+
schema: {
380+
myProperty: {
381+
"default": [],
382+
parse: function () {
383+
return [true];
384+
}
385+
},
386+
string: { type: 'string' },
387+
num: 0
388+
},
389+
init: function () {
390+
this.data.num = 0;
391+
this.el.setAttribute('custom-attribute', 'custom-value');
392+
},
393+
update: function () { },
394+
tick: function () { },
395+
remove: function () { },
396+
pause: function () { },
397+
play: function () { },
398+
multiply: function (f) {
399+
// Reference to system because both were registered with the same name.
400+
return f * this.data.num * this.system.data.counter;
401+
}
402+
});
308403

309404

310405
//// [neverReturningFunctions1.d.ts]
311-
declare function fail(message?: string): never;
312-
declare function f01(x: string | undefined): void;
313-
declare function f02(x: number): number;
314-
declare function f03(x: string): void;
315-
declare function f11(x: string | undefined, fail: (message?: string) => never): void;
316-
declare function f12(x: number, fail: (message?: string) => never): number;
317-
declare function f13(x: string, fail: (message?: string) => never): void;
318-
declare namespace Debug {
319-
function fail(message?: string): never;
406+
export interface Component<T extends object = any> {
407+
attrName?: string;
408+
data: T;
409+
dependencies?: string[];
410+
el: any;
411+
id: string;
412+
multiple?: boolean;
413+
name: string;
414+
schema: unknown;
415+
system: any;
416+
init(data?: T): void;
417+
pause(): void;
418+
play(): void;
419+
remove(): void;
420+
tick?(time: number, timeDelta: number): void;
421+
update(oldData: T): void;
422+
updateSchema?(): void;
423+
extendSchema(update: unknown): void;
424+
flushToDOM(): void;
320425
}
321-
declare function f21(x: string | undefined): void;
322-
declare function f22(x: number): number;
323-
declare function f23(x: string): void;
324-
declare function f24(x: string): void;
325-
declare class Test {
326-
fail(message?: string): never;
327-
f1(x: string | undefined): void;
328-
f2(x: number): number;
329-
f3(x: string): void;
426+
export interface ComponentConstructor<T extends object> {
427+
new (el: unknown, attrValue: string, id: string): T & Component;
428+
prototype: T & {
429+
name: string;
430+
system: unknown;
431+
play(): void;
432+
pause(): void;
433+
};
330434
}
331-
declare function f30(x: string | number | undefined): void;
332-
declare function f31(x: {
333-
a: string | number;
334-
}): void;
335-
declare function f40(x: number): void;
336-
declare function f41(x: number): void;
337-
declare function f42(x: number): void;
435+
export declare type ComponentDefinition<T extends object = object> = T & Partial<Component> & ThisType<T & Component>;

0 commit comments

Comments
 (0)