mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-03-15 05:55:11 -05:00
Merge pull request #13903 from Microsoft/jsPropertyWidening
Widen special JS property declarations to match regular property declarations
This commit is contained in:
@@ -3259,7 +3259,7 @@ namespace ts {
|
||||
type;
|
||||
}
|
||||
|
||||
function getTypeForVariableLikeDeclarationFromJSDocComment(declaration: VariableLikeDeclaration) {
|
||||
function getTypeForDeclarationFromJSDocComment(declaration: Node ) {
|
||||
const jsdocType = getJSDocType(declaration);
|
||||
if (jsdocType) {
|
||||
return getTypeFromTypeNode(jsdocType);
|
||||
@@ -3287,7 +3287,7 @@ namespace ts {
|
||||
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
|
||||
// one as its type), otherwise fallback to the below standard TS codepaths to
|
||||
// try to figure it out.
|
||||
const type = getTypeForVariableLikeDeclarationFromJSDocComment(declaration);
|
||||
const type = getTypeForDeclarationFromJSDocComment(declaration);
|
||||
if (type && type !== unknownType) {
|
||||
return type;
|
||||
}
|
||||
@@ -3382,6 +3382,27 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Return the inferred type for a variable, parameter, or property declaration
|
||||
function getTypeForJSSpecialPropertyDeclaration(declaration: Declaration): Type {
|
||||
const expression = declaration.kind === SyntaxKind.BinaryExpression ? <BinaryExpression>declaration :
|
||||
declaration.kind === SyntaxKind.PropertyAccessExpression ? <BinaryExpression>getAncestor(declaration, SyntaxKind.BinaryExpression) :
|
||||
undefined;
|
||||
|
||||
if (!expression) {
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
if (expression.flags & NodeFlags.JavaScriptFile) {
|
||||
// If there is a JSDoc type, use it
|
||||
const type = getTypeForDeclarationFromJSDocComment(expression.parent);
|
||||
if (type && type !== unknownType) {
|
||||
return getWidenedType(type);
|
||||
}
|
||||
}
|
||||
|
||||
return getWidenedLiteralType(checkExpressionCached(expression.right));
|
||||
}
|
||||
|
||||
// Return the type implied by a binding pattern element. This is the type of the initializer of the element if
|
||||
// one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding
|
||||
// pattern. Otherwise, it is the type any.
|
||||
@@ -3536,18 +3557,7 @@ namespace ts {
|
||||
// * className.prototype.method = expr
|
||||
if (declaration.kind === SyntaxKind.BinaryExpression ||
|
||||
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
// Use JS Doc type if present on parent expression statement
|
||||
if (declaration.flags & NodeFlags.JavaScriptFile) {
|
||||
const jsdocType = getJSDocType(declaration.parent);
|
||||
if (jsdocType) {
|
||||
return links.type = getTypeFromTypeNode(jsdocType);
|
||||
}
|
||||
}
|
||||
const declaredTypes = map(symbol.declarations,
|
||||
decl => decl.kind === SyntaxKind.BinaryExpression ?
|
||||
checkExpressionCached((<BinaryExpression>decl).right) :
|
||||
checkExpressionCached((<BinaryExpression>decl.parent).right));
|
||||
type = getUnionType(declaredTypes, /*subtypeReduction*/ true);
|
||||
type = getWidenedType(getUnionType(map(symbol.declarations, getTypeForJSSpecialPropertyDeclaration), /*subtypeReduction*/ true));
|
||||
}
|
||||
else {
|
||||
type = getWidenedTypeForVariableLikeDeclaration(<VariableLikeDeclaration>declaration, /*reportErrors*/ true);
|
||||
@@ -3590,7 +3600,7 @@ namespace ts {
|
||||
const setter = <AccessorDeclaration>getDeclarationOfKind(symbol, SyntaxKind.SetAccessor);
|
||||
|
||||
if (getter && getter.flags & NodeFlags.JavaScriptFile) {
|
||||
const jsDocType = getTypeForVariableLikeDeclarationFromJSDocComment(getter);
|
||||
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
|
||||
if (jsDocType) {
|
||||
return links.type = jsDocType;
|
||||
}
|
||||
|
||||
17
tests/baselines/reference/jsFileClassPropertyType.errors.txt
Normal file
17
tests/baselines/reference/jsFileClassPropertyType.errors.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
tests/cases/compiler/bar.ts(2,1): error TS2322: Type '"string"' is not assignable to type 'number'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/foo.js (0 errors) ====
|
||||
|
||||
class C {
|
||||
constructor () {
|
||||
this.p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
==== tests/cases/compiler/bar.ts (1 errors) ====
|
||||
|
||||
(new C()).p = "string";
|
||||
~~~~~~~~~~~
|
||||
!!! error TS2322: Type '"string"' is not assignable to type 'number'.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
tests/cases/compiler/bar.ts(2,18): error TS2345: Argument of type '"string"' is not assignable to parameter of type 'number'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/foo.js (0 errors) ====
|
||||
|
||||
class C {
|
||||
constructor() {
|
||||
/** @type {number[]}*/
|
||||
this.p = [];
|
||||
}
|
||||
}
|
||||
|
||||
==== tests/cases/compiler/bar.ts (1 errors) ====
|
||||
|
||||
(new C()).p.push("string");
|
||||
~~~~~~~~
|
||||
!!! error TS2345: Argument of type '"string"' is not assignable to parameter of type 'number'.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
tests/cases/compiler/bar.ts(2,1): error TS2322: Type '"string"' is not assignable to type 'number'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/foo.js (0 errors) ====
|
||||
|
||||
class C {
|
||||
constructor() {
|
||||
if (cond) {
|
||||
this.p = null;
|
||||
}
|
||||
else {
|
||||
this.p = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
==== tests/cases/compiler/bar.ts (1 errors) ====
|
||||
|
||||
(new C()).p = "string"; // Error
|
||||
~~~~~~~~~~~
|
||||
!!! error TS2322: Type '"string"' is not assignable to type 'number'.
|
||||
|
||||
@@ -15,39 +15,39 @@ function MyClass() {
|
||||
* @returns {MyClass}
|
||||
*/
|
||||
MyClass.prototype.optionalParam = function(required, notRequired) {
|
||||
>MyClass.prototype.optionalParam = function(required, notRequired) { return this;} : (required: string, notRequired?: string) => { prop: null; optionalParam: any; }
|
||||
>MyClass.prototype.optionalParam = function(required, notRequired) { return this;} : (required: string, notRequired?: string) => { prop: any; optionalParam: any; }
|
||||
>MyClass.prototype.optionalParam : any
|
||||
>MyClass.prototype : any
|
||||
>MyClass : () => void
|
||||
>prototype : any
|
||||
>optionalParam : any
|
||||
>function(required, notRequired) { return this;} : (required: string, notRequired?: string) => { prop: null; optionalParam: any; }
|
||||
>function(required, notRequired) { return this;} : (required: string, notRequired?: string) => { prop: any; optionalParam: any; }
|
||||
>required : string
|
||||
>notRequired : string
|
||||
|
||||
return this;
|
||||
>this : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>this : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
|
||||
};
|
||||
let pInst = new MyClass();
|
||||
>pInst : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>new MyClass() : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>new MyClass() : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>MyClass : () => void
|
||||
|
||||
let c1 = pInst.optionalParam('hello')
|
||||
>c1 : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam('hello') : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam : (required: string, notRequired?: string) => { prop: null; optionalParam: any; }
|
||||
>pInst : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>optionalParam : (required: string, notRequired?: string) => { prop: null; optionalParam: any; }
|
||||
>c1 : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam('hello') : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam : (required: string, notRequired?: string) => { prop: any; optionalParam: any; }
|
||||
>pInst : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>optionalParam : (required: string, notRequired?: string) => { prop: any; optionalParam: any; }
|
||||
>'hello' : "hello"
|
||||
|
||||
let c2 = pInst.optionalParam('hello', null)
|
||||
>c2 : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam('hello', null) : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam : (required: string, notRequired?: string) => { prop: null; optionalParam: any; }
|
||||
>pInst : { prop: null; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>optionalParam : (required: string, notRequired?: string) => { prop: null; optionalParam: any; }
|
||||
>c2 : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam('hello', null) : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>pInst.optionalParam : (required: string, notRequired?: string) => { prop: any; optionalParam: any; }
|
||||
>pInst : { prop: any; optionalParam: (required: string, notRequired?: string) => typeof MyClass; }
|
||||
>optionalParam : (required: string, notRequired?: string) => { prop: any; optionalParam: any; }
|
||||
>'hello' : "hello"
|
||||
>null : null
|
||||
|
||||
|
||||
13
tests/cases/compiler/jsFileClassPropertyType.ts
Normal file
13
tests/cases/compiler/jsFileClassPropertyType.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// @allowJs: true
|
||||
// @noEmit: true
|
||||
|
||||
// @filename: foo.js
|
||||
class C {
|
||||
constructor () {
|
||||
this.p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// @filename: bar.ts
|
||||
|
||||
(new C()).p = "string";
|
||||
14
tests/cases/compiler/jsFileClassPropertyType2.ts
Normal file
14
tests/cases/compiler/jsFileClassPropertyType2.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// @allowJs: true
|
||||
// @noEmit: true
|
||||
|
||||
// @filename: foo.js
|
||||
class C {
|
||||
constructor() {
|
||||
/** @type {number[]}*/
|
||||
this.p = [];
|
||||
}
|
||||
}
|
||||
|
||||
// @filename: bar.ts
|
||||
|
||||
(new C()).p.push("string");
|
||||
18
tests/cases/compiler/jsFileClassPropertyType3.ts
Normal file
18
tests/cases/compiler/jsFileClassPropertyType3.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// @allowJs: true
|
||||
// @noEmit: true
|
||||
|
||||
// @filename: foo.js
|
||||
class C {
|
||||
constructor() {
|
||||
if (cond) {
|
||||
this.p = null;
|
||||
}
|
||||
else {
|
||||
this.p = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @filename: bar.ts
|
||||
|
||||
(new C()).p = "string"; // Error
|
||||
@@ -12,4 +12,4 @@
|
||||
//// let x = new Person(100);
|
||||
//// x.canVote/**/;
|
||||
|
||||
verify.quickInfoAt("", "(property) Person.canVote: true | 23");
|
||||
verify.quickInfoAt("", "(property) Person.canVote: number | boolean");
|
||||
|
||||
Reference in New Issue
Block a user