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

Lines changed: 155 additions & 37 deletions
Large diffs are not rendered by default.

src/compiler/commandLineParser.ts

Lines changed: 5 additions & 0 deletions
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

Lines changed: 32 additions & 1 deletion
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 13 additions & 9 deletions
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 6 additions & 3 deletions
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

Lines changed: 17 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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 {

0 commit comments

Comments
 (0)