Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This function types #6739

Merged
merged 46 commits into from
Apr 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0a968f0
Parse this type using parameter syntax
sandersn Jan 29, 2016
d8a77c0
Check this type in functions.
sandersn Jan 29, 2016
a639b71
Skip emit of this types as first parameter.
sandersn Jan 29, 2016
9bd7afb
Add new error message and strictThis flag
sandersn Jan 29, 2016
ca16209
Make compiler strictThis clean.
sandersn Jan 29, 2016
22e571f
Add services support for this types.
sandersn Jan 29, 2016
5fe8478
Add overloads for Function.apply/call/bind
sandersn Jan 29, 2016
04e7d81
Add tests and baselines for this-function types.
sandersn Jan 29, 2016
a4f1154
Fix free function bug in cachingInServerLSHost
sandersn Jan 29, 2016
d030889
Update baselines
sandersn Jan 29, 2016
675e081
Make this-type of bind's return explicit
sandersn Feb 2, 2016
f6361ce
Undo strictThis-clean changes
sandersn Feb 3, 2016
8032b06
Merge branch 'master' into this-function-types
sandersn Feb 3, 2016
0af56c0
Update error numbers in new tests after merge
sandersn Feb 3, 2016
8c87da5
First round of review comments addressed.
sandersn Feb 4, 2016
2f74da1
Add specific error messages for out-of-place this
sandersn Feb 5, 2016
71488fc
Refactorings from review comments
sandersn Feb 5, 2016
5821b87
Do not contextually type object callee arguments
sandersn Feb 5, 2016
80de700
Get contextual type of this parameter correctly
sandersn Feb 6, 2016
fa59875
Improve display and contextual typing of `this`
sandersn Feb 8, 2016
738713b
Improve error reporting
sandersn Feb 8, 2016
41bb446
Revert unioning of this argument types
sandersn Feb 9, 2016
a014edf
Address more comments and remove temp test.
sandersn Feb 16, 2016
e7aa7e4
Merge branch 'master' into this-function-types
sandersn Feb 16, 2016
482accc
Union this-types of unioned call signatures
sandersn Mar 8, 2016
7b531fc
Check this expressions in object literal methods
sandersn Mar 9, 2016
4012587
Update baselines: 'this' in object literal methods
sandersn Mar 9, 2016
3297824
Add missed update of thisInObjectLiterals baseline
sandersn Mar 9, 2016
fa22250
Merge branch 'master' into this-function-types
sandersn Mar 9, 2016
3a46e72
After merge, update error numbers in baselines
sandersn Mar 9, 2016
1032cc5
Rename --strictThis to --strictThisChecks
sandersn Mar 11, 2016
c9f5f3d
Remove --strictThisChecks
sandersn Mar 25, 2016
a91cdcc
Add --noImplicitThis flag
sandersn Mar 25, 2016
9e5f260
Merge branch 'master' into this-function-types
sandersn Mar 28, 2016
f64110a
Update baselines after merging from master
sandersn Mar 28, 2016
0113ad5
Error on all uses of this that are implicitly any
sandersn Mar 30, 2016
e4ed7f9
Address PR comments
sandersn Mar 30, 2016
0060b4d
Test that signature help doesn't show 'this'
sandersn Mar 31, 2016
da98258
Improve error messages and code style
sandersn Mar 31, 2016
ce68932
Merge branch 'master' into this-function-types
sandersn Mar 31, 2016
81f0d86
Fix up baselines and missing , after merge
sandersn Mar 31, 2016
4197a30
Improve error messages and always return any from newed functions
sandersn Mar 31, 2016
9e5fba6
Prepend 'the' to a couple of ambiguous messages.
sandersn Apr 1, 2016
2a9f39b
Forbid ConstructType as part of 'no this in constructors'
sandersn Apr 1, 2016
921d5f8
Fix == typo and add object literal 'this' test
sandersn Apr 1, 2016
6c735b5
Add contextual typing test with `this` specified
sandersn Apr 7, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 155 additions & 37 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ namespace ts {
type: "boolean",
description: Diagnostics.Raise_error_on_expressions_and_declarations_with_an_implied_any_type,
},
{
name: "noImplicitThis",
type: "boolean",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a description for this one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

description: Diagnostics.Raise_error_on_this_expressions_with_an_implied_any_type,
},
{
name: "noLib",
type: "boolean",
Expand Down
33 changes: 32 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1879,6 +1879,34 @@
"category": "Error",
"code": 2678
},
"A function that is called with the 'new' keyword cannot have a 'this' type that is 'void'.": {
"category": "Error",
"code": 2679
},
"A 'this' parameter must be the first parameter.": {
"category": "Error",
"code": 2680
},
"A constructor cannot have a 'this' parameter.": {
"category": "Error",
"code": 2681
},
"A setter cannot have a 'this' parameter.": {
"category": "Error",
"code": 2682
},
"'this' implicitly has type 'any' because it does not have a type annotation.": {
"category": "Error",
"code": 2683
},
"The 'this' context of type '{0}' is not assignable to method's 'this' of type '{1}'.": {
"category": "Error",
"code": 2684
},
"The 'this' types of each signature are incompatible.": {
"category": "Error",
"code": 2685
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
Expand Down Expand Up @@ -2632,7 +2660,10 @@
"category": "Error",
"code": 6114
},

"Raise error on 'this' expressions with an implied 'any' type.": {
"category": "Message",
"code": 6115
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4630,8 +4630,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
write("(");
if (node) {
const parameters = node.parameters;
const skipCount = node.parameters.length && (<Identifier>node.parameters[0].name).text === "this" ? 1 : 0;
const omitCount = languageVersion < ScriptTarget.ES6 && hasRestParameter(node) ? 1 : 0;
emitList(parameters, 0, parameters.length - omitCount, /*multiLine*/ false, /*trailingComma*/ false);
emitList(parameters, skipCount, parameters.length - omitCount - skipCount, /*multiLine*/ false, /*trailingComma*/ false);
}
write(")");
decreaseIndent();
Expand Down
22 changes: 13 additions & 9 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2010,7 +2010,7 @@ namespace ts {
}

function isStartOfParameter(): boolean {
return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token) || token === SyntaxKind.AtToken;
return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token) || token === SyntaxKind.AtToken || token === SyntaxKind.ThisKeyword;
}

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

function parseParameter(): ParameterDeclaration {
const node = <ParameterDeclaration>createNode(SyntaxKind.Parameter);
if (token === SyntaxKind.ThisKeyword) {
node.name = createIdentifier(/*isIdentifier*/true, undefined);
node.type = parseParameterType();
return finishNode(node);
}

node.decorators = parseDecorators();
setModifiers(node, parseModifiers());
node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken);

// FormalParameter [Yield,Await]:
// BindingElement[?Yield,?Await]

node.name = parseIdentifierOrPattern();

if (getFullWidth(node.name) === 0 && node.flags === 0 && isModifierKind(token)) {
// in cases like
// 'use strict'
Expand Down Expand Up @@ -2068,11 +2072,11 @@ namespace ts {
}

function fillSignature(
returnToken: SyntaxKind,
yieldContext: boolean,
awaitContext: boolean,
requireCompleteParameterList: boolean,
signature: SignatureDeclaration): void {
returnToken: SyntaxKind,
yieldContext: boolean,
awaitContext: boolean,
requireCompleteParameterList: boolean,
signature: SignatureDeclaration): void {

const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken;
signature.typeParameters = parseTypeParameters();
Expand Down Expand Up @@ -2473,7 +2477,7 @@ namespace ts {
// Skip modifiers
parseModifiers();
}
if (isIdentifier()) {
if (isIdentifier() || token === SyntaxKind.ThisKeyword) {
nextToken();
return true;
}
Expand Down
4 changes: 3 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1778,7 +1778,7 @@ namespace ts {
buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildDisplayForParametersAndDelimiters(parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildDisplayForParametersAndDelimiters(thisType: Type, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void;
}
Expand Down Expand Up @@ -2280,6 +2280,7 @@ namespace ts {
declaration: SignatureDeclaration; // Originating declaration
typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic)
parameters: Symbol[]; // Parameters
thisType?: Type; // type of this-type
/* @internal */
resolvedReturnType: Type; // Resolved return type
/* @internal */
Expand Down Expand Up @@ -2424,6 +2425,7 @@ namespace ts {
noEmitOnError?: boolean;
noErrorTruncation?: boolean;
noImplicitAny?: boolean;
noImplicitThis?: boolean;
noLib?: boolean;
noResolve?: boolean;
out?: string;
Expand Down
4 changes: 2 additions & 2 deletions src/harness/loggedIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ namespace Playback {
), path));

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

wrapper.exit = (exitCode) => {
if (recordLog !== undefined) {
Expand Down
9 changes: 6 additions & 3 deletions src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,22 +215,25 @@ interface Function {
* @param thisArg The object to be used as the this object.
* @param argArray A set of arguments to be passed to the function.
*/
apply(thisArg: any, argArray?: any): any;
apply<T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U;
apply(this: Function, thisArg: any, argArray?: any): any;

/**
* Calls a method of an object, substituting another object for the current object.
* @param thisArg The object to be used as the current object.
* @param argArray A list of arguments to be passed to the method.
*/
call(thisArg: any, ...argArray: any[]): any;
call<T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, ...argArray: any[]): U;
call(this: Function, thisArg: any, ...argArray: any[]): any;

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

prototype: any;
readonly length: number;
Expand Down
21 changes: 17 additions & 4 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,7 @@ namespace ts {
declaration: SignatureDeclaration;
typeParameters: TypeParameter[];
parameters: Symbol[];
thisType: Type;
resolvedReturnType: Type;
minArgumentCount: number;
hasRestParameter: boolean;
Expand Down Expand Up @@ -4109,6 +4110,9 @@ namespace ts {
if (typeChecker.isArgumentsSymbol(symbol)) {
return ScriptElementKind.localVariableElement;
}
if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) {
return ScriptElementKind.parameterElement;
}
if (flags & SymbolFlags.Variable) {
if (isFirstDeclarationOfSymbolParameter(symbol)) {
return ScriptElementKind.parameterElement;
Expand Down Expand Up @@ -4171,6 +4175,7 @@ namespace ts {
const symbolFlags = symbol.flags;
let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(symbol, symbolFlags, location);
let hasAddedSymbolInfo: boolean;
const isThisExpression: boolean = location.kind === SyntaxKind.ThisKeyword && isExpression(location);
let type: Type;

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

let signature: Signature;
type = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location);
if (type) {
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
const right = (<PropertyAccessExpression>location.parent).name;
Expand Down Expand Up @@ -4292,7 +4297,7 @@ namespace ts {
}
}
}
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo) {
if (symbolFlags & SymbolFlags.Class && !hasAddedSymbolInfo && !isThisExpression) {
if (getDeclarationOfKind(symbol, SyntaxKind.ClassExpression)) {
// Special case for class expressions because we would like to indicate that
// the class name is local to the class body (similar to function expression)
Expand Down Expand Up @@ -4434,11 +4439,19 @@ namespace ts {
if (!hasAddedSymbolInfo) {
if (symbolKind !== ScriptElementKind.unknown) {
if (type) {
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
if (isThisExpression) {
addNewLineIfDisplayPartsExist();
displayParts.push(keywordPart(SyntaxKind.ThisKeyword));
}
else {
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
}

// For properties, variables and local vars: show the type
if (symbolKind === ScriptElementKind.memberVariableElement ||
symbolFlags & SymbolFlags.Variable ||
symbolKind === ScriptElementKind.localVariableElement) {
symbolKind === ScriptElementKind.localVariableElement ||
isThisExpression) {
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
displayParts.push(spacePart());
// If the type is type parameter, format it specially
Expand Down
2 changes: 1 addition & 1 deletion src/services/signatureHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ namespace ts.SignatureHelp {
signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray;
suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken));
let parameterParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.parameters, writer, invocation));
typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation));
addRange(suffixDisplayParts, parameterParts);
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ tests/cases/compiler/assignmentToObjectAndFunction.ts(8,5): error TS2322: Type '
Property 'apply' is missing in type '{}'.
tests/cases/compiler/assignmentToObjectAndFunction.ts(29,5): error TS2322: Type 'typeof bad' is not assignable to type 'Function'.
Types of property 'apply' are incompatible.
Type 'number' is not assignable to type '(thisArg: any, argArray?: any) => any'.
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/cases/compiler/assignmentToObjectAndFunction.ts (3 errors) ====
Expand Down Expand Up @@ -48,4 +48,4 @@ tests/cases/compiler/assignmentToObjectAndFunction.ts(29,5): error TS2322: Type
~~~~~~~~~~
!!! error TS2322: Type 'typeof bad' is not assignable to type 'Function'.
!!! error TS2322: Types of property 'apply' are incompatible.
!!! error TS2322: Type 'number' is not assignable to type '(thisArg: any, argArray?: any) => any'.
!!! 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; }'.
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class C {

var fn = async () => await other.apply(this, arguments);
>fn : Symbol(fn, Decl(asyncArrowFunctionCapturesArguments_es6.ts, 3, 9))
>other.apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --))
>other.apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>other : Symbol(other, Decl(asyncArrowFunctionCapturesArguments_es6.ts, 1, 13))
>apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --))
>apply : Symbol(Function.apply, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>this : Symbol(C, Decl(asyncArrowFunctionCapturesArguments_es6.ts, 0, 0))
>arguments : Symbol(arguments)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ class C {
>other : () => void

var fn = async () => await other.apply(this, arguments);
>fn : () => Promise<any>
>async () => await other.apply(this, arguments) : () => Promise<any>
>await other.apply(this, arguments) : any
>other.apply(this, arguments) : any
>other.apply : (thisArg: any, argArray?: any) => any
>fn : () => Promise<void>
>async () => await other.apply(this, arguments) : () => Promise<void>
>await other.apply(this, arguments) : void
>other.apply(this, arguments) : void
>other.apply : { <T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U; (this: Function, thisArg: any, argArray?: any): any; }
>other : () => void
>apply : (thisArg: any, argArray?: any) => any
>apply : { <T, U>(this: (this: T, ...argArray: any[]) => U, thisArg: T, argArray?: any): U; (this: Function, thisArg: any, argArray?: any): any; }
>this : this
>arguments : IArguments
}
Expand Down
7 changes: 7 additions & 0 deletions tests/baselines/reference/commentsOnObjectLiteral3.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ var v = {
>a : Symbol(a, Decl(commentsOnObjectLiteral3.ts, 8, 13), Decl(commentsOnObjectLiteral3.ts, 12, 18))

return this.prop;
>this.prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))
>this : Symbol(, Decl(commentsOnObjectLiteral3.ts, 1, 7))
>prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))

} /*trailing 1*/,
//setter
set a(value) {
>a : Symbol(a, Decl(commentsOnObjectLiteral3.ts, 8, 13), Decl(commentsOnObjectLiteral3.ts, 12, 18))
>value : Symbol(value, Decl(commentsOnObjectLiteral3.ts, 14, 7))

this.prop = value;
>this.prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))
>this : Symbol(, Decl(commentsOnObjectLiteral3.ts, 1, 7))
>prop : Symbol(prop, Decl(commentsOnObjectLiteral3.ts, 1, 9))
>value : Symbol(value, Decl(commentsOnObjectLiteral3.ts, 14, 7))

} // trailing 2
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/commentsOnObjectLiteral3.types
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ var v = {
>a : any

return this.prop;
>this.prop : any
>this : any
>prop : any
>this.prop : number
>this : { prop: number; func: () => void; func1(): void; a: any; }
>prop : number

} /*trailing 1*/,
//setter
Expand All @@ -36,9 +36,9 @@ var v = {

this.prop = value;
>this.prop = value : any
>this.prop : any
>this : any
>prop : any
>this.prop : number
>this : { prop: number; func: () => void; func1(): void; a: any; }
>prop : number
>value : any

} // trailing 2
Expand Down
7 changes: 4 additions & 3 deletions tests/baselines/reference/commentsOnObjectLiteral4.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ var v = {
* @type {number}
*/
get bar(): number {
return this._bar;
return 12;
}
}
}


//// [commentsOnObjectLiteral4.js]
var v = {
/**
* @type {number}
*/
get bar() {
return this._bar;
return 12;
}
};
3 changes: 2 additions & 1 deletion tests/baselines/reference/commentsOnObjectLiteral4.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var v = {
get bar(): number {
>bar : Symbol(bar, Decl(commentsOnObjectLiteral4.ts, 1, 9))

return this._bar;
return 12;
}
}

Loading