Skip to content

Commit ff1f337

Browse files
committed
Improve contextual types and return type checking
1 parent 9e985c9 commit ff1f337

File tree

9 files changed

+200
-133
lines changed

9 files changed

+200
-133
lines changed

src/compiler/checker.ts

+78-84
Large diffs are not rendered by default.

src/compiler/utilities.ts

+43-9
Original file line numberDiff line numberDiff line change
@@ -2620,20 +2620,19 @@ namespace ts {
26202620
});
26212621
}
26222622

2623-
/** Get the type annotation for the value parameter. */
2624-
export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration, includeJSDocType?: boolean): TypeNode {
2623+
function getSetAccessorValueParameter(accessor: SetAccessorDeclaration): ParameterDeclaration | undefined {
26252624
if (accessor && accessor.parameters.length > 0) {
26262625
const hasThis = accessor.parameters.length === 2 && parameterIsThisKeyword(accessor.parameters[0]);
2627-
const parameter = accessor.parameters[hasThis ? 1 : 0];
2628-
if (parameter.type) {
2629-
return parameter.type;
2630-
}
2631-
if (includeJSDocType && parameter.flags & NodeFlags.JavaScriptFile) {
2632-
return getJSDocType(parameter);
2633-
}
2626+
return accessor.parameters[hasThis ? 1 : 0];
26342627
}
26352628
}
26362629

2630+
/** Get the type annotation for the value parameter. */
2631+
export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration): TypeNode {
2632+
const parameter = getSetAccessorValueParameter(accessor);
2633+
return parameter && parameter.type;
2634+
}
2635+
26372636
export function getThisParameter(signature: SignatureDeclaration): ParameterDeclaration | undefined {
26382637
if (signature.parameters.length) {
26392638
const thisParameter = signature.parameters[0];
@@ -2712,6 +2711,41 @@ namespace ts {
27122711
};
27132712
}
27142713

2714+
/**
2715+
* Gets the effective type annotation of a variable, parameter, or property. If the node was
2716+
* parsed in a JavaScript file, gets the type annotation from JSDoc.
2717+
*/
2718+
export function getEffectiveTypeAnnotationNode(node: VariableLikeDeclaration): TypeNode {
2719+
if (node.type) {
2720+
return node.type;
2721+
}
2722+
if (node.flags & NodeFlags.JavaScriptFile) {
2723+
return getJSDocType(node);
2724+
}
2725+
}
2726+
2727+
/**
2728+
* Gets the effective return type annotation of a signature. If the node was parsed in a
2729+
* JavaScript file, gets the return type annotation from JSDoc.
2730+
*/
2731+
export function getEffectiveReturnTypeNode(node: SignatureDeclaration): TypeNode {
2732+
if (node.type) {
2733+
return node.type;
2734+
}
2735+
if (node.flags & NodeFlags.JavaScriptFile) {
2736+
return getJSDocReturnType(node);
2737+
}
2738+
}
2739+
2740+
/**
2741+
* Gets the effective type annotation of the value parameter of a set accessor. If the node
2742+
* was parsed in a JavaScript file, gets the type annotation from JSDoc.
2743+
*/
2744+
export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration): TypeNode {
2745+
const parameter = getSetAccessorValueParameter(node);
2746+
return parameter && getEffectiveTypeAnnotationNode(parameter);
2747+
}
2748+
27152749
export function emitNewLineBeforeLeadingComments(lineMap: number[], writer: EmitTextWriter, node: TextRange, leadingComments: CommentRange[]) {
27162750
emitNewLineBeforeLeadingCommentsOfPosition(lineMap, writer, node.pos, leadingComments);
27172751
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tests/cases/conformance/jsdoc/returns.js(6,5): error TS2322: Type '5' is not assignable to type 'string'.
2+
tests/cases/conformance/jsdoc/returns.js(13,5): error TS2322: Type 'true | 5' is not assignable to type 'string | number'.
3+
Type 'true' is not assignable to type 'string | number'.
4+
5+
6+
==== tests/cases/conformance/jsdoc/returns.js (2 errors) ====
7+
// @ts-check
8+
/**
9+
* @returns {string} This comment is not currently exposed
10+
*/
11+
function f() {
12+
return 5;
13+
~~~~~~~~~
14+
!!! error TS2322: Type '5' is not assignable to type 'string'.
15+
}
16+
17+
/**
18+
* @returns {string | number} This comment is not currently exposed
19+
*/
20+
function f1() {
21+
return 5 || true;
22+
~~~~~~~~~~~~~~~~~
23+
!!! error TS2322: Type 'true | 5' is not assignable to type 'string | number'.
24+
!!! error TS2322: Type 'true' is not assignable to type 'string | number'.
25+
}

tests/baselines/reference/checkJsdocTypeTag2.errors.txt

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
tests/cases/conformance/jsdoc/0.js(3,5): error TS2322: Type 'true' is not assignable to type 'string'.
22
tests/cases/conformance/jsdoc/0.js(6,5): error TS2322: Type '"hello"' is not assignable to type 'number'.
33
tests/cases/conformance/jsdoc/0.js(10,4): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'number'.
4-
tests/cases/conformance/jsdoc/0.js(13,7): error TS2451: Cannot redeclare block-scoped variable 'x2'.
54
tests/cases/conformance/jsdoc/0.js(17,1): error TS2322: Type 'number' is not assignable to type 'string'.
6-
tests/cases/conformance/jsdoc/0.js(20,7): error TS2451: Cannot redeclare block-scoped variable 'x2'.
75
tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does not exist on type 'number'.
6+
tests/cases/conformance/jsdoc/0.js(24,7): error TS2322: Type '(a: number) => number' is not assignable to type '(arg0: number) => string'.
7+
Type 'number' is not assignable to type 'string'.
88

99

10-
==== tests/cases/conformance/jsdoc/0.js (7 errors) ====
10+
==== tests/cases/conformance/jsdoc/0.js (6 errors) ====
1111
// @ts-check
1212
/** @type {String} */
1313
var S = true;
@@ -27,8 +27,6 @@ tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does
2727

2828
/** @type {function (number): number} */
2929
const x2 = (a) => a + 1;
30-
~~
31-
!!! error TS2451: Cannot redeclare block-scoped variable 'x2'.
3230

3331
/** @type {string} */
3432
var a;
@@ -37,9 +35,14 @@ tests/cases/conformance/jsdoc/0.js(20,21): error TS2339: Property 'concat' does
3735
!!! error TS2322: Type 'number' is not assignable to type 'string'.
3836

3937
/** @type {function (number): number} */
40-
const x2 = (a) => a.concat("hi");
41-
~~
42-
!!! error TS2451: Cannot redeclare block-scoped variable 'x2'.
38+
const x3 = (a) => a.concat("hi");
4339
~~~~~~
4440
!!! error TS2339: Property 'concat' does not exist on type 'number'.
45-
x2(0);
41+
x3(0);
42+
43+
/** @type {function (number): string} */
44+
const x4 = (a) => a + 1;
45+
~~
46+
!!! error TS2322: Type '(a: number) => number' is not assignable to type '(arg0: number) => string'.
47+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
48+
x4(0);

tests/baselines/reference/checkJsdocTypeTag2.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ var a;
1818
a = x2(0);
1919

2020
/** @type {function (number): number} */
21-
const x2 = (a) => a.concat("hi");
22-
x2(0);
21+
const x3 = (a) => a.concat("hi");
22+
x3(0);
23+
24+
/** @type {function (number): string} */
25+
const x4 = (a) => a + 1;
26+
x4(0);
2327

2428
//// [0.js]
2529
// @ts-check
@@ -36,5 +40,8 @@ var x2 = function (a) { return a + 1; };
3640
var a;
3741
a = x2(0);
3842
/** @type {function (number): number} */
39-
var x2 = function (a) { return a.concat("hi"); };
40-
x2(0);
43+
var x3 = function (a) { return a.concat("hi"); };
44+
x3(0);
45+
/** @type {function (number): string} */
46+
var x4 = function (a) { return a + 1; };
47+
x4(0);

tests/baselines/reference/contextualTypeFromJSDoc.symbols

+5-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const arr = [
1111

1212
];
1313

14-
/** @return {function(): Array<[string, {x?:number, y?:number}]>} */
14+
/** @return {Array<[string, {x?:number, y?:number}]>} */
1515
function f() {
1616
>f : Symbol(f, Decl(index.js, 4, 2))
1717

@@ -28,13 +28,13 @@ function f() {
2828
class C {
2929
>C : Symbol(C, Decl(index.js, 12, 1))
3030

31-
/** @param {function(): Array<[string, {x?:number, y?:number}]>} value */
31+
/** @param {Array<[string, {x?:number, y?:number}]>} value */
3232
set x(value) { }
33-
>x : Symbol(C.x, Decl(index.js, 14, 9))
33+
>x : Symbol(C.x, Decl(index.js, 14, 9), Decl(index.js, 16, 20))
3434
>value : Symbol(value, Decl(index.js, 16, 10))
3535

36-
get () {
37-
>get : Symbol(C.get, Decl(index.js, 16, 20))
36+
get x() {
37+
>x : Symbol(C.x, Decl(index.js, 14, 9), Decl(index.js, 16, 20))
3838

3939
return [
4040
['a', { x: 1 }],

tests/baselines/reference/contextualTypeFromJSDoc.types

+17-17
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,24 @@ const arr = [
2020

2121
];
2222

23-
/** @return {function(): Array<[string, {x?:number, y?:number}]>} */
23+
/** @return {Array<[string, {x?:number, y?:number}]>} */
2424
function f() {
25-
>f : () => () => [string, { x?: number; y?: number; }][]
25+
>f : () => [string, { x?: number; y?: number; }][]
2626

2727
return [
28-
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ((string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[])[]
28+
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ([string, { x: number; }] | [string, { y: number; }])[]
2929

3030
['a', { x: 1 }],
31-
>['a', { x: 1 }] : (string | { [x: string]: any; x: number; })[]
31+
>['a', { x: 1 }] : [string, { x: number; }]
3232
>'a' : "a"
33-
>{ x: 1 } : { [x: string]: any; x: number; }
33+
>{ x: 1 } : { x: number; }
3434
>x : number
3535
>1 : 1
3636

3737
['b', { y: 2 }]
38-
>['b', { y: 2 }] : (string | { [x: string]: any; y: number; })[]
38+
>['b', { y: 2 }] : [string, { y: number; }]
3939
>'b' : "b"
40-
>{ y: 2 } : { [x: string]: any; y: number; }
40+
>{ y: 2 } : { y: number; }
4141
>y : number
4242
>2 : 2
4343

@@ -47,28 +47,28 @@ function f() {
4747
class C {
4848
>C : C
4949

50-
/** @param {function(): Array<[string, {x?:number, y?:number}]>} value */
50+
/** @param {Array<[string, {x?:number, y?:number}]>} value */
5151
set x(value) { }
52-
>x : any
53-
>value : () => [string, { x?: number; y?: number; }][]
52+
>x : [string, { x?: number; y?: number; }][]
53+
>value : [string, { x?: number; y?: number; }][]
5454

55-
get () {
56-
>get : () => ((string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[])[]
55+
get x() {
56+
>x : [string, { x?: number; y?: number; }][]
5757

5858
return [
59-
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ((string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[])[]
59+
>[ ['a', { x: 1 }], ['b', { y: 2 }] ] : ([string, { x: number; }] | [string, { y: number; }])[]
6060

6161
['a', { x: 1 }],
62-
>['a', { x: 1 }] : (string | { [x: string]: any; x: number; })[]
62+
>['a', { x: 1 }] : [string, { x: number; }]
6363
>'a' : "a"
64-
>{ x: 1 } : { [x: string]: any; x: number; }
64+
>{ x: 1 } : { x: number; }
6565
>x : number
6666
>1 : 1
6767

6868
['b', { y: 2 }]
69-
>['b', { y: 2 }] : (string | { [x: string]: any; y: number; })[]
69+
>['b', { y: 2 }] : [string, { y: number; }]
7070
>'b' : "b"
71-
>{ y: 2 } : { [x: string]: any; y: number; }
71+
>{ y: 2 } : { y: number; }
7272
>y : number
7373
>2 : 2
7474

tests/cases/conformance/jsdoc/checkJsdocTypeTag2.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,9 @@ var a;
2121
a = x2(0);
2222

2323
/** @type {function (number): number} */
24-
const x2 = (a) => a.concat("hi");
25-
x2(0);
24+
const x3 = (a) => a.concat("hi");
25+
x3(0);
26+
27+
/** @type {function (number): string} */
28+
const x4 = (a) => a + 1;
29+
x4(0);

tests/cases/conformance/types/contextualTypes/jsdoc/contextualTypeFromJSDoc.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const arr = [
1010
['b', { y: 2 }]
1111
];
1212

13-
/** @return {function(): Array<[string, {x?:number, y?:number}]>} */
13+
/** @return {Array<[string, {x?:number, y?:number}]>} */
1414
function f() {
1515
return [
1616
['a', { x: 1 }],
@@ -19,9 +19,9 @@ function f() {
1919
}
2020

2121
class C {
22-
/** @param {function(): Array<[string, {x?:number, y?:number}]>} value */
22+
/** @param {Array<[string, {x?:number, y?:number}]>} value */
2323
set x(value) { }
24-
get () {
24+
get x() {
2525
return [
2626
['a', { x: 1 }],
2727
['b', { y: 2 }]

0 commit comments

Comments
 (0)