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

Strict function types #18654

Merged
merged 25 commits into from
Oct 2, 2017
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
12f5dd8
Introduce --strictFunctionTypes mode
ahejlsberg Sep 18, 2017
f8ff7f7
Use dedicated marker types for variance determination
ahejlsberg Sep 18, 2017
670d711
Add quick path for computing array variance as it is already known
ahejlsberg Sep 18, 2017
a0fa69f
Handle contravariance in type inference
ahejlsberg Sep 19, 2017
b58e0fb
Add comments
ahejlsberg Sep 19, 2017
84f7afd
Handle special case of 'void' type arguments for covariant type param…
ahejlsberg Sep 19, 2017
54eadef
Accept new baselines
ahejlsberg Sep 19, 2017
44cc8c5
Use methods in dom.generated.d.ts to opt out of strict checks
ahejlsberg Sep 19, 2017
dd466ae
Update tsconfig baselines
ahejlsberg Sep 19, 2017
24698dd
Revert dom.generated.d.ts and fix duplicate declarations
ahejlsberg Sep 20, 2017
f8e2cc1
Properly flag and structurally compare marker type references
ahejlsberg Sep 21, 2017
589e1f4
Update comment
ahejlsberg Sep 21, 2017
afc8a26
Always perform structural comparison when variance check fails
ahejlsberg Sep 22, 2017
70e8f73
Add tests
ahejlsberg Sep 22, 2017
91691f6
Strict function type checking only for certain function types
ahejlsberg Sep 25, 2017
6a481e8
Update tests
ahejlsberg Sep 25, 2017
1795614
Accept new baselines
ahejlsberg Sep 26, 2017
5613be4
Only methods and constructors are bivariant in --strictFunctionTypes …
ahejlsberg Sep 28, 2017
1609196
Accept new baselines
ahejlsberg Sep 28, 2017
0756aa1
Merge branch 'master' into strictFunctionTypes
ahejlsberg Sep 28, 2017
c626d9d
Accept new baselines
ahejlsberg Sep 28, 2017
936f98d
Addressing CR feedback
ahejlsberg Sep 29, 2017
bf75a3f
Emit .d.ts file in test
ahejlsberg Oct 2, 2017
bff843a
Improve error elaboration for invariant generic types
ahejlsberg Oct 2, 2017
c2344e0
Add error elaboration test
ahejlsberg Oct 2, 2017
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
199 changes: 175 additions & 24 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ namespace ts {
category: Diagnostics.Strict_Type_Checking_Options,
description: Diagnostics.Enable_strict_null_checks
},
{
name: "strictFunctionTypes",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
description: Diagnostics.Enable_strict_checking_of_function_types
},
{
name: "noImplicitThis",
type: "boolean",
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3318,6 +3318,10 @@
"category": "Message",
"code": 6185
},
"Enable strict checking of function types.": {
"category": "Message",
"code": 6186
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
Expand Down
20 changes: 16 additions & 4 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3215,6 +3215,7 @@ namespace ts {
NonPrimitive = 1 << 24, // intrinsic object type
/* @internal */
JsxAttributes = 1 << 25, // Jsx attributes type
MarkerType = 1 << 26, // Marker type used for variance probing

/* @internal */
Nullable = Undefined | Null,
Expand Down Expand Up @@ -3343,10 +3344,19 @@ namespace ts {
typeArguments?: Type[]; // Type reference type arguments (undefined if none)
}

export const enum Variance {
Copy link
Member

Choose a reason for hiding this comment

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

should be probably @internal

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

Invariant = 0, // Neither covariant nor contravariant
Covariant = 1, // Covariant
Contravariant = 2, // Contravariant
Bivariant = 3, // Both covariant and contravariant
Independent = 4, // Unwitnessed type parameter
}

// Generic class and interface types
export interface GenericType extends InterfaceType, TypeReference {
/* @internal */
instantiations: Map<TypeReference>; // Generic instantiation cache
instantiations: Map<TypeReference>; // Generic instantiation cache
variances?: Variance[]; // Variance of each type parameter
}

export interface UnionOrIntersectionType extends Type {
Expand Down Expand Up @@ -3522,9 +3532,10 @@ namespace ts {
}

export const enum InferencePriority {
Copy link
Member

Choose a reason for hiding this comment

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

should probably be @internal

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably, but not really related to this PR since the type already existed before. I'm going to leave it be for now.

NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
MappedType = 1 << 1, // Reverse inference for mapped type
ReturnType = 1 << 2, // Inference made from return type of generic function
Contravariant = 1 << 0, // Inference from contravariant position
NakedTypeVariable = 1 << 1, // Naked type variable in union or intersection type
MappedType = 1 << 2, // Reverse inference for mapped type
ReturnType = 1 << 3, // Inference made from return type of generic function
}

export interface InferenceInfo {
Expand Down Expand Up @@ -3707,6 +3718,7 @@ namespace ts {
sourceMap?: boolean;
sourceRoot?: string;
strict?: boolean;
strictFunctionTypes?: boolean; // Always combine with strict property
strictNullChecks?: boolean; // Always combine with strict property
/* @internal */ stripInternal?: boolean;
suppressExcessPropertyErrors?: boolean;
Expand Down
22 changes: 1 addition & 21 deletions src/lib/dom.generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4233,11 +4233,7 @@ interface HTMLBodyElement extends HTMLElement {
onafterprint: (this: HTMLBodyElement, ev: Event) => any;
onbeforeprint: (this: HTMLBodyElement, ev: Event) => any;
onbeforeunload: (this: HTMLBodyElement, ev: BeforeUnloadEvent) => any;
onblur: (this: HTMLBodyElement, ev: FocusEvent) => any;
onerror: (this: HTMLBodyElement, ev: ErrorEvent) => any;
onfocus: (this: HTMLBodyElement, ev: FocusEvent) => any;
onhashchange: (this: HTMLBodyElement, ev: HashChangeEvent) => any;
onload: (this: HTMLBodyElement, ev: Event) => any;
onmessage: (this: HTMLBodyElement, ev: MessageEvent) => any;
onoffline: (this: HTMLBodyElement, ev: Event) => any;
ononline: (this: HTMLBodyElement, ev: Event) => any;
Expand All @@ -4246,7 +4242,6 @@ interface HTMLBodyElement extends HTMLElement {
onpageshow: (this: HTMLBodyElement, ev: PageTransitionEvent) => any;
onpopstate: (this: HTMLBodyElement, ev: PopStateEvent) => any;
onresize: (this: HTMLBodyElement, ev: UIEvent) => any;
onscroll: (this: HTMLBodyElement, ev: UIEvent) => any;
onstorage: (this: HTMLBodyElement, ev: StorageEvent) => any;
onunload: (this: HTMLBodyElement, ev: Event) => any;
text: any;
Expand Down Expand Up @@ -4901,10 +4896,6 @@ interface HTMLFrameElement extends HTMLElement, GetSVGDocument {
* Sets or retrieves whether the user can resize the frame.
*/
noResize: boolean;
/**
* Raised when the object has been completely received from the server.
*/
onload: (this: HTMLFrameElement, ev: Event) => any;
/**
* Sets or retrieves whether the frame can be scrolled.
*/
Expand Down Expand Up @@ -4970,17 +4961,10 @@ interface HTMLFrameSetElement extends HTMLElement {
onafterprint: (this: HTMLFrameSetElement, ev: Event) => any;
onbeforeprint: (this: HTMLFrameSetElement, ev: Event) => any;
onbeforeunload: (this: HTMLFrameSetElement, ev: BeforeUnloadEvent) => any;
/**
* Fires when the object loses the input focus.
*/
onblur: (this: HTMLFrameSetElement, ev: FocusEvent) => any;
onerror: (this: HTMLFrameSetElement, ev: ErrorEvent) => any;
/**
* Fires when the object receives focus.
*/
onfocus: (this: HTMLFrameSetElement, ev: FocusEvent) => any;
onhashchange: (this: HTMLFrameSetElement, ev: HashChangeEvent) => any;
onload: (this: HTMLFrameSetElement, ev: Event) => any;
onmessage: (this: HTMLFrameSetElement, ev: MessageEvent) => any;
onoffline: (this: HTMLFrameSetElement, ev: Event) => any;
ononline: (this: HTMLFrameSetElement, ev: Event) => any;
Expand All @@ -4989,7 +4973,6 @@ interface HTMLFrameSetElement extends HTMLElement {
onpageshow: (this: HTMLFrameSetElement, ev: PageTransitionEvent) => any;
onpopstate: (this: HTMLFrameSetElement, ev: PopStateEvent) => any;
onresize: (this: HTMLFrameSetElement, ev: UIEvent) => any;
onscroll: (this: HTMLFrameSetElement, ev: UIEvent) => any;
onstorage: (this: HTMLFrameSetElement, ev: StorageEvent) => any;
onunload: (this: HTMLFrameSetElement, ev: Event) => any;
/**
Expand Down Expand Up @@ -5125,10 +5108,7 @@ interface HTMLIFrameElement extends HTMLElement, GetSVGDocument {
* Sets or retrieves whether the user can resize the frame.
*/
noResize: boolean;
/**
* Raised when the object has been completely received from the server.
*/
onload: (this: HTMLIFrameElement, ev: Event) => any;

readonly sandbox: DOMSettableTokenList;
/**
* Sets or retrieves whether the frame can be scrolled.
Expand Down
8 changes: 6 additions & 2 deletions tests/baselines/reference/commentsClassMembers.js
Original file line number Diff line number Diff line change
Expand Up @@ -548,11 +548,15 @@ declare class c1 {
}
declare var i1: c1;
declare var i1_p: number;
declare var i1_f: (b: number) => number;
declare var i1_f: {
(b: number): number;
};
declare var i1_r: number;
declare var i1_prop: number;
declare var i1_nc_p: number;
declare var i1_ncf: (b: number) => number;
declare var i1_ncf: {
(b: number): number;
};
declare var i1_ncr: number;
declare var i1_ncprop: number;
declare var i1_s_p: number;
Expand Down
8 changes: 6 additions & 2 deletions tests/baselines/reference/commentsInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,13 @@ declare var i2_i_nc_x: number;
declare var i2_i_nc_foo: (b: number) => string;
declare var i2_i_nc_foo_r: string;
declare var i2_i_r: number;
declare var i2_i_fnfoo: (b: number) => string;
declare var i2_i_fnfoo: {
(b: number): string;
};
declare var i2_i_fnfoo_r: string;
declare var i2_i_nc_fnfoo: (b: number) => string;
declare var i2_i_nc_fnfoo: {
(b: number): string;
};
declare var i2_i_nc_fnfoo_r: string;
interface i3 {
/** Comment i3 x*/
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/fuzzy.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ tests/cases/compiler/fuzzy.ts(21,13): error TS2322: Type '{ anything: number; on
Types of property 'oneI' are incompatible.
Type 'this' is not assignable to type 'I'.
Type 'C' is not assignable to type 'I'.
Property 'alsoWorks' is missing in type 'C'.
tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' cannot be converted to type 'R'.
Property 'anything' is missing in type '{ oneI: this; }'.

Expand Down Expand Up @@ -38,6 +39,7 @@ tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' canno
!!! error TS2322: Types of property 'oneI' are incompatible.
!!! error TS2322: Type 'this' is not assignable to type 'I'.
!!! error TS2322: Type 'C' is not assignable to type 'I'.
!!! error TS2322: Property 'alsoWorks' is missing in type 'C'.
}

worksToo():R {
Expand Down
30 changes: 25 additions & 5 deletions tests/baselines/reference/jsDocTypeTag2.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,18 @@
"text": " ",
"kind": "space"
},
{
"text": "{",
"kind": "punctuation"
},
{
"text": "\n",
"kind": "lineBreak"
},
{
"text": " ",
"kind": "space"
},
{
"text": "(",
"kind": "punctuation"
Expand All @@ -497,11 +509,7 @@
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "=>",
"text": ":",
"kind": "punctuation"
},
{
Expand All @@ -511,6 +519,18 @@
{
"text": "number",
"kind": "keyword"
},
{
"text": ";",
"kind": "punctuation"
},
{
"text": "\n",
"kind": "lineBreak"
},
{
"text": "}",
"kind": "punctuation"
}
],
"documentation": [],
Expand Down
25 changes: 25 additions & 0 deletions tests/baselines/reference/strictFunctionTypes1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//// [strictFunctionTypes1.ts]
declare function f1<T>(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void;
declare function f2<T>(obj: T, f1: (x: T) => void, f2: (x: T) => void): T;
declare function f3<T>(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T;

interface Func<T> { (x: T): void }

declare function f4<T>(f1: Func<T>, f2: Func<T>): Func<T>;

declare function fo(x: Object): void;
declare function fs(x: string): void;
declare function fx(f: (x: "def") => void): void;

const x1 = f1(fo, fs); // (x: string) => void
const x2 = f2("abc", fo, fs); // "abc"
const x3 = f3("abc", fo, fx); // "abc" | "def"
const x4 = f4(fo, fs); // Func<string>


//// [strictFunctionTypes1.js]
"use strict";
var x1 = f1(fo, fs); // (x: string) => void
var x2 = f2("abc", fo, fs); // "abc"
var x3 = f3("abc", fo, fx); // "abc" | "def"
var x4 = f4(fo, fs); // Func<string>
96 changes: 96 additions & 0 deletions tests/baselines/reference/strictFunctionTypes1.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
=== tests/cases/compiler/strictFunctionTypes1.ts ===
declare function f1<T>(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void;
>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 0, 0))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20))
>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 0, 23))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 0, 28))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20))
>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 0, 42))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 0, 48))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 0, 65))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20))

declare function f2<T>(obj: T, f1: (x: T) => void, f2: (x: T) => void): T;
>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 0, 79))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20))
>obj : Symbol(obj, Decl(strictFunctionTypes1.ts, 1, 23))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20))
>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 1, 30))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 1, 36))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20))
>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 1, 50))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 1, 56))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20))

declare function f3<T>(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T;
>f3 : Symbol(f3, Decl(strictFunctionTypes1.ts, 1, 74))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20))
>obj : Symbol(obj, Decl(strictFunctionTypes1.ts, 2, 23))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20))
>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 2, 30))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 2, 36))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20))
>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 2, 50))
>f : Symbol(f, Decl(strictFunctionTypes1.ts, 2, 56))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 2, 60))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20))

interface Func<T> { (x: T): void }
>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 4, 15))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 4, 21))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 4, 15))

declare function f4<T>(f1: Func<T>, f2: Func<T>): Func<T>;
>f4 : Symbol(f4, Decl(strictFunctionTypes1.ts, 4, 34))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20))
>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 6, 23))
>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20))
>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 6, 35))
>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20))
>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87))
>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20))

declare function fo(x: Object): void;
>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 8, 20))
>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))

declare function fs(x: string): void;
>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 9, 20))

declare function fx(f: (x: "def") => void): void;
>fx : Symbol(fx, Decl(strictFunctionTypes1.ts, 9, 37))
>f : Symbol(f, Decl(strictFunctionTypes1.ts, 10, 20))
>x : Symbol(x, Decl(strictFunctionTypes1.ts, 10, 24))

const x1 = f1(fo, fs); // (x: string) => void
>x1 : Symbol(x1, Decl(strictFunctionTypes1.ts, 12, 5))
>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 0, 0))
>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58))
>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37))

const x2 = f2("abc", fo, fs); // "abc"
>x2 : Symbol(x2, Decl(strictFunctionTypes1.ts, 13, 5))
>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 0, 79))
>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58))
>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37))

const x3 = f3("abc", fo, fx); // "abc" | "def"
>x3 : Symbol(x3, Decl(strictFunctionTypes1.ts, 14, 5))
>f3 : Symbol(f3, Decl(strictFunctionTypes1.ts, 1, 74))
>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58))
>fx : Symbol(fx, Decl(strictFunctionTypes1.ts, 9, 37))

const x4 = f4(fo, fs); // Func<string>
>x4 : Symbol(x4, Decl(strictFunctionTypes1.ts, 15, 5))
>f4 : Symbol(f4, Decl(strictFunctionTypes1.ts, 4, 34))
>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58))
>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37))

Loading