Skip to content

Commit 3704ad7

Browse files
committed
Merge pull request #6739 from Microsoft/this-function-types
This function types
2 parents 3acff6d + 6c735b5 commit 3704ad7

File tree

73 files changed

+4507
-150
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+4507
-150
lines changed

src/compiler/checker.ts

+155-37
Large diffs are not rendered by default.

src/compiler/commandLineParser.ts

+5
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ namespace ts {
122122
type: "boolean",
123123
description: Diagnostics.Raise_error_on_expressions_and_declarations_with_an_implied_any_type,
124124
},
125+
{
126+
name: "noImplicitThis",
127+
type: "boolean",
128+
description: Diagnostics.Raise_error_on_this_expressions_with_an_implied_any_type,
129+
},
125130
{
126131
name: "noLib",
127132
type: "boolean",

src/compiler/diagnosticMessages.json

+32-1
Original file line numberDiff line numberDiff line change
@@ -1879,6 +1879,34 @@
18791879
"category": "Error",
18801880
"code": 2678
18811881
},
1882+
"A function that is called with the 'new' keyword cannot have a 'this' type that is 'void'.": {
1883+
"category": "Error",
1884+
"code": 2679
1885+
},
1886+
"A 'this' parameter must be the first parameter.": {
1887+
"category": "Error",
1888+
"code": 2680
1889+
},
1890+
"A constructor cannot have a 'this' parameter.": {
1891+
"category": "Error",
1892+
"code": 2681
1893+
},
1894+
"A setter cannot have a 'this' parameter.": {
1895+
"category": "Error",
1896+
"code": 2682
1897+
},
1898+
"'this' implicitly has type 'any' because it does not have a type annotation.": {
1899+
"category": "Error",
1900+
"code": 2683
1901+
},
1902+
"The 'this' context of type '{0}' is not assignable to method's 'this' of type '{1}'.": {
1903+
"category": "Error",
1904+
"code": 2684
1905+
},
1906+
"The 'this' types of each signature are incompatible.": {
1907+
"category": "Error",
1908+
"code": 2685
1909+
},
18821910
"Import declaration '{0}' is using private name '{1}'.": {
18831911
"category": "Error",
18841912
"code": 4000
@@ -2632,7 +2660,10 @@
26322660
"category": "Error",
26332661
"code": 6114
26342662
},
2635-
2663+
"Raise error on 'this' expressions with an implied 'any' type.": {
2664+
"category": "Message",
2665+
"code": 6115
2666+
},
26362667
"Variable '{0}' implicitly has an '{1}' type.": {
26372668
"category": "Error",
26382669
"code": 7005

src/compiler/emitter.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4630,8 +4630,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
46304630
write("(");
46314631
if (node) {
46324632
const parameters = node.parameters;
4633+
const skipCount = node.parameters.length && (<Identifier>node.parameters[0].name).text === "this" ? 1 : 0;
46334634
const omitCount = languageVersion < ScriptTarget.ES6 && hasRestParameter(node) ? 1 : 0;
4634-
emitList(parameters, 0, parameters.length - omitCount, /*multiLine*/ false, /*trailingComma*/ false);
4635+
emitList(parameters, skipCount, parameters.length - omitCount - skipCount, /*multiLine*/ false, /*trailingComma*/ false);
46354636
}
46364637
write(")");
46374638
decreaseIndent();

src/compiler/parser.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -2010,7 +2010,7 @@ namespace ts {
20102010
}
20112011

20122012
function isStartOfParameter(): boolean {
2013-
return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token) || token === SyntaxKind.AtToken;
2013+
return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token) || token === SyntaxKind.AtToken || token === SyntaxKind.ThisKeyword;
20142014
}
20152015

20162016
function setModifiers(node: Node, modifiers: ModifiersArray) {
@@ -2022,15 +2022,19 @@ namespace ts {
20222022

20232023
function parseParameter(): ParameterDeclaration {
20242024
const node = <ParameterDeclaration>createNode(SyntaxKind.Parameter);
2025+
if (token === SyntaxKind.ThisKeyword) {
2026+
node.name = createIdentifier(/*isIdentifier*/true, undefined);
2027+
node.type = parseParameterType();
2028+
return finishNode(node);
2029+
}
2030+
20252031
node.decorators = parseDecorators();
20262032
setModifiers(node, parseModifiers());
20272033
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);
20282034

20292035
// FormalParameter [Yield,Await]:
20302036
// BindingElement[?Yield,?Await]
2031-
20322037
node.name = parseIdentifierOrPattern();
2033-
20342038
if (getFullWidth(node.name) === 0 && node.flags === 0 && isModifierKind(token)) {
20352039
// in cases like
20362040
// 'use strict'
@@ -2068,11 +2072,11 @@ namespace ts {
20682072
}
20692073

20702074
function fillSignature(
2071-
returnToken: SyntaxKind,
2072-
yieldContext: boolean,
2073-
awaitContext: boolean,
2074-
requireCompleteParameterList: boolean,
2075-
signature: SignatureDeclaration): void {
2075+
returnToken: SyntaxKind,
2076+
yieldContext: boolean,
2077+
awaitContext: boolean,
2078+
requireCompleteParameterList: boolean,
2079+
signature: SignatureDeclaration): void {
20762080

20772081
const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken;
20782082
signature.typeParameters = parseTypeParameters();
@@ -2473,7 +2477,7 @@ namespace ts {
24732477
// Skip modifiers
24742478
parseModifiers();
24752479
}
2476-
if (isIdentifier()) {
2480+
if (isIdentifier() || token === SyntaxKind.ThisKeyword) {
24772481
nextToken();
24782482
return true;
24792483
}

src/compiler/types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1778,7 +1778,7 @@ namespace ts {
17781778
buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
17791779
buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
17801780
buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
1781-
buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
1781+
buildDisplayForParametersAndDelimiters(thisType: Type, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
17821782
buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
17831783
buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
17841784
}
@@ -2280,6 +2280,7 @@ namespace ts {
22802280
declaration: SignatureDeclaration; // Originating declaration
22812281
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
22822282
parameters: Symbol[]; // Parameters
2283+
thisType?: Type; // type of this-type
22832284
/* @internal */
22842285
resolvedReturnType: Type; // Resolved return type
22852286
/* @internal */
@@ -2424,6 +2425,7 @@ namespace ts {
24242425
noEmitOnError?: boolean;
24252426
noErrorTruncation?: boolean;
24262427
noImplicitAny?: boolean;
2428+
noImplicitThis?: boolean;
24272429
noLib?: boolean;
24282430
noResolve?: boolean;
24292431
out?: string;

src/harness/loggedIO.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,8 @@ namespace Playback {
237237
), path));
238238

239239
wrapper.writeFile = recordReplay(wrapper.writeFile, underlying)(
240-
(path, contents) => callAndRecord(underlying.writeFile(path, contents), recordLog.filesWritten, { path, contents, bom: false }),
241-
(path, contents) => noOpReplay("writeFile"));
240+
(path: string, contents: string) => callAndRecord(underlying.writeFile(path, contents), recordLog.filesWritten, { path, contents, bom: false }),
241+
(path: string, contents: string) => noOpReplay("writeFile"));
242242

243243
wrapper.exit = (exitCode) => {
244244
if (recordLog !== undefined) {

src/lib/es5.d.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -215,22 +215,25 @@ interface Function {
215215
* @param thisArg The object to be used as the this object.
216216
* @param argArray A set of arguments to be passed to the function.
217217
*/
218-
apply(thisArg: any, argArray?: any): any;
218+
apply<T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U;
219+
apply(this: Function, thisArg: any, argArray?: any): any;
219220

220221
/**
221222
* Calls a method of an object, substituting another object for the current object.
222223
* @param thisArg The object to be used as the current object.
223224
* @param argArray A list of arguments to be passed to the method.
224225
*/
225-
call(thisArg: any, ...argArray: any[]): any;
226+
call<T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, ...argArray: any[]): U;
227+
call(this: Function, thisArg: any, ...argArray: any[]): any;
226228

227229
/**
228230
* For a given function, creates a bound function that has the same body as the original function.
229231
* The this object of the bound function is associated with the specified object, and has the specified initial parameters.
230232
* @param thisArg An object to which the this keyword can refer inside the new function.
231233
* @param argArray A list of arguments to be passed to the new function.
232234
*/
233-
bind(thisArg: any, ...argArray: any[]): any;
235+
bind<T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, ...argArray: any[]): (this: void, ...argArray: any[]) => U;
236+
bind(this: Function, thisArg: any, ...argArray: any[]): any;
234237

235238
prototype: any;
236239
readonly length: number;

src/services/services.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ namespace ts {
741741
declaration: SignatureDeclaration;
742742
typeParameters: TypeParameter[];
743743
parameters: Symbol[];
744+
thisType: Type;
744745
resolvedReturnType: Type;
745746
minArgumentCount: number;
746747
hasRestParameter: boolean;
@@ -4109,6 +4110,9 @@ namespace ts {
41094110
if (typeChecker.isArgumentsSymbol(symbol)) {
41104111
return ScriptElementKind.localVariableElement;
41114112
}
4113+
if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) {
4114+
return ScriptElementKind.parameterElement;
4115+
}
41124116
if (flags & SymbolFlags.Variable) {
41134117
if (isFirstDeclarationOfSymbolParameter(symbol)) {
41144118
return ScriptElementKind.parameterElement;
@@ -4171,6 +4175,7 @@ namespace ts {
41714175
const symbolFlags = symbol.flags;
41724176
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, symbolFlags, location);
41734177
let hasAddedSymbolInfo: boolean;
4178+
const isThisExpression: boolean = location.kind === SyntaxKind.ThisKeyword && isExpression(location);
41744179
let type: Type;
41754180

41764181
// Class at constructor site need to be shown as constructor apart from property,method, vars
@@ -4181,7 +4186,7 @@ namespace ts {
41814186
}
41824187

41834188
let signature: Signature;
4184-
type = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
4189+
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location);
41854190
if (type) {
41864191
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
41874192
const right = (<PropertyAccessExpression>location.parent).name;
@@ -4292,7 +4297,7 @@ namespace ts {
42924297
}
42934298
}
42944299
}
4295-
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) {
4300+
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo && !isThisExpression) {
42964301
if (getDeclarationOfKind(symbol, SyntaxKind.ClassExpression)) {
42974302
// Special case for class expressions because we would like to indicate that
42984303
// the class name is local to the class body (similar to function expression)
@@ -4434,11 +4439,19 @@ namespace ts {
44344439
if (!hasAddedSymbolInfo) {
44354440
if (symbolKind !== ScriptElementKind.unknown) {
44364441
if (type) {
4437-
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
4442+
if (isThisExpression) {
4443+
addNewLineIfDisplayPartsExist();
4444+
displayParts.push(keywordPart(SyntaxKind.ThisKeyword));
4445+
}
4446+
else {
4447+
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
4448+
}
4449+
44384450
// For properties, variables and local vars: show the type
44394451
if (symbolKind === ScriptElementKind.memberVariableElement ||
44404452
symbolFlags & SymbolFlags.Variable ||
4441-
symbolKind === ScriptElementKind.localVariableElement) {
4453+
symbolKind === ScriptElementKind.localVariableElement ||
4454+
isThisExpression) {
44424455
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
44434456
displayParts.push(spacePart());
44444457
// If the type is type parameter, format it specially

src/services/signatureHelp.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ namespace ts.SignatureHelp {
559559
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
560560
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
561561
let parameterParts = mapToDisplayParts(writer =>
562-
typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.parameters, writer, invocation));
562+
typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation));
563563
addRange(suffixDisplayParts, parameterParts);
564564
}
565565
else {

tests/baselines/reference/assignmentToObjectAndFunction.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ tests/cases/compiler/assignmentToObjectAndFunction.ts(8,5): error TS2322: Type '
55
Property 'apply' is missing in type '{}'.
66
tests/cases/compiler/assignmentToObjectAndFunction.ts(29,5): error TS2322: Type 'typeof bad' is not assignable to type 'Function'.
77
Types of property 'apply' are incompatible.
8-
Type 'number' is not assignable to type '(thisArg: any, argArray?: any) => any'.
8+
Type 'number' is not assignable to type '{ <T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U; (this: Function, thisArg: any, argArray?: any): any; }'.
99

1010

1111
==== tests/cases/compiler/assignmentToObjectAndFunction.ts (3 errors) ====
@@ -48,4 +48,4 @@ tests/cases/compiler/assignmentToObjectAndFunction.ts(29,5): error TS2322: Type
4848
~~~~~~~~~~
4949
!!! error TS2322: Type 'typeof bad' is not assignable to type 'Function'.
5050
!!! error TS2322: Types of property 'apply' are incompatible.
51-
!!! error TS2322: Type 'number' is not assignable to type '(thisArg: any, argArray?: any) => any'.
51+
!!! error TS2322: Type 'number' is not assignable to type '{ <T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U; (this: Function, thisArg: any, argArray?: any): any; }'.

tests/baselines/reference/asyncArrowFunctionCapturesArguments_es6.symbols

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ class C {
1010

1111
var fn = async () => await other.apply(this, arguments);
1212
>fn : Symbol(fn, Decl(asyncArrowFunctionCapturesArguments_es6.ts, 3, 9))
13-
>other.apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --))
13+
>other.apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
1414
>other : Symbol(other, Decl(asyncArrowFunctionCapturesArguments_es6.ts, 1, 13))
15-
>apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --))
15+
>apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
1616
>this : Symbol(C, Decl(asyncArrowFunctionCapturesArguments_es6.ts, 0, 0))
1717
>arguments : Symbol(arguments)
1818
}

tests/baselines/reference/asyncArrowFunctionCapturesArguments_es6.types

+6-6
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ class C {
99
>other : () => void
1010

1111
var fn = async () => await other.apply(this, arguments);
12-
>fn : () => Promise<any>
13-
>async () => await other.apply(this, arguments) : () => Promise<any>
14-
>await other.apply(this, arguments) : any
15-
>other.apply(this, arguments) : any
16-
>other.apply : (thisArg: any, argArray?: any) => any
12+
>fn : () => Promise<void>
13+
>async () => await other.apply(this, arguments) : () => Promise<void>
14+
>await other.apply(this, arguments) : void
15+
>other.apply(this, arguments) : void
16+
>other.apply : { <T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U; (this: Function, thisArg: any, argArray?: any): any; }
1717
>other : () => void
18-
>apply : (thisArg: any, argArray?: any) => any
18+
>apply : { <T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U; (this: Function, thisArg: any, argArray?: any): any; }
1919
>this : this
2020
>arguments : IArguments
2121
}

tests/baselines/reference/commentsOnObjectLiteral3.symbols

+7
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@ var v = {
2121
>a : Symbol(a, Decl(commentsOnObjectLiteral3.ts, 8, 13), Decl(commentsOnObjectLiteral3.ts, 12, 18))
2222

2323
return this.prop;
24+
>this.prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))
25+
>this : Symbol(, Decl(commentsOnObjectLiteral3.ts, 1, 7))
26+
>prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))
27+
2428
} /*trailing 1*/,
2529
//setter
2630
set a(value) {
2731
>a : Symbol(a, Decl(commentsOnObjectLiteral3.ts, 8, 13), Decl(commentsOnObjectLiteral3.ts, 12, 18))
2832
>value : Symbol(value, Decl(commentsOnObjectLiteral3.ts, 14, 7))
2933

3034
this.prop = value;
35+
>this.prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))
36+
>this : Symbol(, Decl(commentsOnObjectLiteral3.ts, 1, 7))
37+
>prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))
3138
>value : Symbol(value, Decl(commentsOnObjectLiteral3.ts, 14, 7))
3239

3340
} // trailing 2

tests/baselines/reference/commentsOnObjectLiteral3.types

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ var v = {
2424
>a : any
2525

2626
return this.prop;
27-
>this.prop : any
28-
>this : any
29-
>prop : any
27+
>this.prop : number
28+
>this : { prop: number; func: () => void; func1(): void; a: any; }
29+
>prop : number
3030

3131
} /*trailing 1*/,
3232
//setter
@@ -36,9 +36,9 @@ var v = {
3636

3737
this.prop = value;
3838
>this.prop = value : any
39-
>this.prop : any
40-
>this : any
41-
>prop : any
39+
>this.prop : number
40+
>this : { prop: number; func: () => void; func1(): void; a: any; }
41+
>prop : number
4242
>value : any
4343

4444
} // trailing 2

tests/baselines/reference/commentsOnObjectLiteral4.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ var v = {
55
* @type {number}
66
*/
77
get bar(): number {
8-
return this._bar;
8+
return 12;
99
}
10-
}
10+
}
11+
1112

1213
//// [commentsOnObjectLiteral4.js]
1314
var v = {
1415
/**
1516
* @type {number}
1617
*/
1718
get bar() {
18-
return this._bar;
19+
return 12;
1920
}
2021
};

tests/baselines/reference/commentsOnObjectLiteral4.symbols

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ var v = {
99
get bar(): number {
1010
>bar : Symbol(bar, Decl(commentsOnObjectLiteral4.ts, 1, 9))
1111

12-
return this._bar;
12+
return 12;
1313
}
1414
}
15+

0 commit comments

Comments
 (0)