启用对 JavaScript 文件的类型检查
在 TypeScript 编译选项 compilerOptions 全部配置项 中,可以通过以下2个属性配置 JavaScript Support:
-
allowJs
- 是否允许编译 JavaScript 文件。
- 默认值是
false
。在默认情况下,TypeScript 编译器只处理.ts
、.tsx
和.d.ts
文件,不会编译.js
文件。
-
checkJs
- 是否对 JavaScript 文件进行类型检查。
- 默认值是 false。在默认情况下,TypeScript 编译器不会对 JavaScript 文件进行类型检查。
在 tsconfig.json
中配置:
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"target": "es5",
"module": "commonjs",
"strict": true
}
}
类型检查的范围和限制
- 类型推断:TypeScript 会尝试对 JavaScript 文件中的变量和函数进行类型推断。例如,如果一个变量被赋值为一个字符串,TypeScript 会推断该变量的类型为字符串。注意:由于 JavaScript 是动态类型语言,类型推断可能不是完全准确的。
- 函数参数和返回值:对于有明确参数和返回值类型注释的 JavaScript 函数,TypeScript 可以进行更严格的类型检查。如果函数的实际参数类型与注释不匹配,或者返回值类型与预期不符,TypeScript 会报告错误。
- 模块导入和导出:当 JavaScript 文件使用模块导入和导出时,TypeScript 可以检查导入和导出的类型是否匹配。但是,如果导入的模块没有类型声明文件(
.d.ts
),类型检查可能会受到限制。
添加注释忽略类型检查
使用 // @ts-nocheck
忽略类型检查
// @ts-nocheck
注释可以用来忽略对特定 JavaScript 文件的类型检查。
如果"allowJs": true,"checkJs": true,
,需要对某个JavaScript文件关闭类型检查,可以在文件的顶部添加 // @ts-nocheck
注释:
// @ts-nocheck
function add(a, b) {
return a + b;
}
const result = add("Hello", 5);
console.log(result);
这样,TypeScript 在处理这个文件时将忽略类型检查,不会报告错误。
使用 // @ts-check
开启类型检查
如果"allowJs": true,"checkJs": false,
,但是要对某个JavaScript文件进行类型检查,可以添加 // @ts-check
注释:
// @ts-check
function multiply(a, b) {
return a * b;
}
const product = multiply(3, "four");
console.log(product);
当 TypeScript 处理这个文件时,会报告类型错误。
使用 // @ts-ignore
忽略特定行的错误
如果"allowJs": true,"checkJs": true,
,需要让 TypeScript 忽略JavaScript文件某一行的类型检查错误,可以该语句上方添加// @ts-ignore
注释:
let a = 10; // ts 类型检查 推断 a 是number
a = 'hello world'; // 报错:不能将类型“string”分配给类型“number”。
// @ts-ignore
a = 'hello world'; // 类型检查错误被忽略,不会报错
// @ts-ignore
仅仅对紧随其后的那一行代码做约束,可以写在代码的任意位置
使用// @ts-expect-error
忽略特定行的错误
如果"allowJs": true,"checkJs": true,
,需要让 TypeScript 忽略JavaScript文件某一行的类型检查错误,可以该语句上方添加// @ts-expect-error
注释:
let a = 10; // ts 类型检查 推断 a 是number
a = 'hello world'; // 报错:不能将类型“string”分配给类型“number”。
// `// @ts-expect-error`
a = 'hello world'; // 类型检查错误被忽略,不会报错
// @ts-expect-error
与 // @ts-ignore
的区别:
如果随后一行代码是没有类型错误,代码提示器就会认为@ts-expect-error
没有被使用。 @ts-ignore
不会有提示。
JavaScript 用 JSDoc 来 明确类型信息
.js
文件里,类型可以和在.ts
文件里一样被推断出来。如同TypeScript,--noImplicitAny
会在编译器无法推断类型的位置报错。
所有JSDoc支持的模式,请看JSDoc中文文档。
使用JSDoc来明确类型信息:
- 使用
@param
标签来指定函数参数的类型 - 使用
@return
标签来指定函数的返回值类型。
示例:
/**
* @param {number} num1 - The first number.
* @param {number} num2 - The second number.
* @return {number} - The sum of the two numbers.
*/
function sum(num1, num2) {
return num1 + num2;
}
// 使用示例
console.log(sum( 'hello', 10 )); // 报错:类型“string”的参数不能赋给类型“number”的参数。
在这个例子中,使用 @param {number} num1
和 @param {number} num2
来指定函数 sum
的两个参数都是数字类型。
sum( 'hello', 10 )
会报错:类型“string”的参数不能赋给类型“number”的参数。
TS支持的JSDoc
下面的列表列出了当前所支持的JSDoc注解,你可以用它们在JavaScript文件里添加类型信息。
注意,没有在下面列出的标记(例如@async
)都是还不支持的。
@type
@param
(or@arg
or@argument
)@returns
(or@return
)@typedef
@callback
@template
@class
(or@constructor
)@this
@extends
(or@augments
)@enum
@type
可以使用@type
标记并引用一个类型名称(原始类型,TypeScript里声明的类型,或在JSDoc里@typedef
标记指定的) 可以使用任何TypeScript类型和大多数JSDoc类型。
语法:
@type {type} - description
示例:
/**
/**
* @type {Object} person
* @property {string} name - The person's name.
* @property {number} age - The person's age.
*/
let person = {
name: 'John',
age: 36,
address: 'xxxxx'
}
变量person
有两个明确的属性,name
是字符串类型,用于表示人的名字;age
是数字类型,用于表示人的年龄。但是又不止这两个属性,没有明确定义的属性address
也不会报错。
指定对象字面量类型(使用 {}
来表示对象类型,并列出对象的属性和类型):
/** @type {{name: string, age: number}} */
let person = { name: 'John', age: 30 };
person = { name: 'John', age: 30, address: 'xxxxx' }; // 报错
有多种方式来指定数组,其中 type
是数组元素的类型:
@type Array<type>
@type Array.<type>
@type {type[]}
示例:
/** @type {Array<number>} */
let arr = [1, 2, 3];
arr.push('abc'); // 报错:类型“string”的参数不能赋给类型“number”的参数。
@type {Array<number>}
指定数组的每项元素的类型是number
。
使用 |
来表示联合类型:
/** @type {Array<number | string>} */
let arr = [1, 2, 3];
arr.push('abc');
现在,数组的每项元素的类型是 number
或 string
。
在类型后面加上 ?
表示该类型是可选的:
/** @type {{name: string, age?: number}} */
let person = { name: 'John' };
person.age = 36;
可以使用字符串和数字索引签名来指定map-like
和array-like
的对象,使用标准的JSDoc语法或者TypeScript语法:
/**
* A map-like object that maps arbitrary `string` properties to `number`s.
*
* @type {Object.<string, number>}
*/
let stringToNumber;
/** @type {Object.<number, object>} */
let arrayLike;
这两个类型与TypeScript里的{ [x: string]: number }
和{ [x: number]: any }
是等同的。编译器能识别出这两种语法。
分析示例:
- 第一个类型注释
- “A map-like object that maps arbitrary string properties to numbers.”这个描述清晰地说明了这个对象的预期用途,即它是一个类似映射(
map
)的对象,将任意的字符串属性映射到数字。 @type {Object.<string, number>}
表示这个变量stringToNumber
被期望是一个对象,其键为字符串类型,值为数字类型。
- “A map-like object that maps arbitrary string properties to numbers.”这个描述清晰地说明了这个对象的预期用途,即它是一个类似映射(
使用示例:
/**
* A map-like object that maps arbitrary `string` properties to `number`s.
*
* @type {Object.<string, number>}
*/
let stringToNumber = {
key1: 10,
key2: 20
};
- 第二个类型注释
@type {Object.<number, object>}
表明变量arrayLike
被期望是一个对象,其键为数字类型,值为任意对象类型。
这种类型注释可以用于表示一个类似数组的对象,其中数字键可以用来模拟数组的索引,而值可以是各种不同类型的对象。
使用示例:
/** @type {Object.<number, object>} */
let arrayLike = {
0: { name: 'obj1' },
1: { name: 'obj2' }
};
指定一个对象是 Window ,该对象应该具有 Window 对象的属性和方法:
/** @type {Window} */
let win = window;
win.alert('Hello!');
指定一个变量 是一个类似于 Promise 的对象,并且这个 Promise 最终会 resolve 为一个字符串值:
/** @type {PromiseLike<string>} */
let promisedString;
在这个示例中:
- Promise 类似对象:
- 在 JavaScript 中,Promise 是一种用于处理异步操作的机制。它代表了一个尚未完成但预期在未来完成的操作,并提供了一种方式来处理操作成功(resolved)或失败(rejected)的情况。
PromiseLike
表示具有与 Promise 类似的行为的对象。它不一定完全符合 Promise 的规范,但可能具有.then()
和.catch()
等方法,用于处理异步操作的结果。
注意:PromiseLike<string>
期望的.then()
方法应该接受两个可选的回调函数,一个用于处理成功的情况,一个用于处理失败的情况。
- 字符串结果:
@type {PromiseLike<string>}
指定了这个 Promise 类似对象最终会产生一个字符串类型的结果。当这个异步操作完成时,可以预期从这个对象中获取一个字符串值。
指定变量是一个HTMLElement
类型的对象:
/** @type {HTMLElement} */
let myElement = document.querySelector(selector);
element.dataset.myData = '';
在这个示例中:
@type {HTMLElement}
表明变量 myElement
被期望是一个 HTMLElement
类型的对象。在 JavaScript 中,HTMLElement
代表 HTML 文档中的一个元素。
使用TypeScript或Closure语法指定函数类型:
/** @type {function(string, boolean): number} Closure syntax */
let sbn;
/** @type {(s: string, b: boolean) => number} Typescript syntax */
let sbn2;
在这个示例中:
- JSDoc 类型注释(Closure 语法)
@type {function(string, boolean): number}
:
这个 JSDoc 类型注释表明变量sbn
被期望是一个函数类型。这个函数接受两个参数,第一个参数是字符串类型(string
),第二个参数是布尔类型(boolean
),并且这个函数返回一个数字类型(number
)的值。
- TypeScript 类型注释语法
@type {(s: string, b: boolean) => number}
:
这是使用 TypeScript 类型注释语法来指定变量sbn2
的类型。同样,它表示sbn2
是一个函数,接受一个字符串参数s
和一个布尔参数b
,并返回一个数字。
用法示例:
/** @type {function(string, boolean): number} Closure syntax */
let sbn = function (str, bool) {
// 根据字符串和布尔值进行一些操作并返回一个数字
if (bool) {
return str.length;
} else {
return 0;
}
};
/** @type {(s: string, b: boolean) => number} Typescript syntax */
let sbn2 = function (s, b) {
return b ? s.length : -1;
};
console.log(sbn('Hello', true)); // 5
console.log(sbn2('World', false)); // -1
直接使用未指定的Function
类型:
/** @type {Function} */
let fnOne;
/** @type {function} */
let fnTwo;
在 JavaScript 中,Function
(or function
)是所有函数的类型。使用这个注释意味着 fnOne
、fnTwo
可以被赋值为任何函数。
Closure的其它类型也可以使用:
/**
* @type {*} - can be 'any' type
*/
let star;
/**
* @type {?} - unknown type (same as 'any')
*/
let question;
类型注释说明:
@type {*}
:
这个 JSDoc 类型注释表明变量star
可以是任何类型。在一些情况下,使用*
表示“any
”类型是为了在文档中明确指出该变量的类型是不确定的或者可以是任意类型。@type {?}
:
这个 JSDoc 类型注释表明变量question
的类型是未知的,并且说它与“any
”类型相同。
转换
在括号表达式前面使用@type
标记,可以将一种类型转换成另一种类型:
/**
* @type {number | string}
*/
// 根据随机数的结果,将变量 numberOrString 赋值为字符串 "hello" 或者数字 100。
let numberOrString = Math.random() < 0.5 ? "hello" : 100;
// 类型断言:将 numberOrString 断言为数字类型,并赋值给 typeAssertedNumber。
let typeAssertedNumber = /** @type {number} */ (numberOrString)
导入类型
可以使用导入类型从其它文件中导入声明。 这个语法是TypeScript特有的,与JSDoc标准不同:
/**
* @param p { import("./a").Pet }
*/
function walk(p) {
console.log(`Walking ${p.name}...`);
}
@param p { import("./a").Pet }
:
这个 JSDoc 类型注释表明函数walk
的参数p
是从模块./a
中导入的类型为Pet
的对象。这指定了参数p
的预期类型。
如果模块 ./a
定义了 Pet
类型如下:
// 模块./a
export class Pet {
constructor(name) {
this.name = name;
}
}
可以这样使用 walk
函数:
import { Pet } from './a';
const myPet = new Pet('Fluffy');
walk(myPet);
导入类型也可以使用在类型别名声明中:
/**
* @typedef Pet { import("./a").Pet }
*/
/**
* @type {Pet}
*/
let myPet; // myPet 的类型 是 Pet 的类型别名。
myPet.name;
@typedef Pet { import("./a").Pet }
:
这里使用@typedef
定义了一个名为Pet
的类型别名。它表示这个新定义的Pet
实际上是从模块./a
中导入的类型。@type {Pet}
这个类型注释表明变量myPet
的类型是前面定义的Pet
类型别名,也就是从模块./a
中导入的那个类型。
导入类型可以用在从模块中得到一个值的类型:
/**
* @type {typeof import("./a").x }
*/
let x = require("./a").x;
@type {typeof import("./a").x }
:
这个 JSDoc 类型注释表明变量x
的类型是模块./a
中导出的变量 x 的类型。具体来说,typeof import("./a").x
获取了模块./a
中x
的类型信息,这里使用这个类型信息来注释变量x
,表示变量x
应该与模块./a
中的x
具有相同的类型。let x = require("./a").x
:
这行代码使用 CommonJS 模块规范的require
函数导入模块./a
,并将模块中导出的x
赋值给变量x
。
@typedef
在 JSDoc 中,@typedef
用于定义一个类型别名。
语法:
@typedef {type} name - description
其中,type
是现有的类型,name
是新定义的类型别名,description
是对这个类型别名的描述。
示例:
/**
* @typedef {Object} Person
* @property {string} name - The person's name.
* @property {number} age - The person's age.
*/
/**
* @param {Person} person - A person object.
*/
function greet(person) {
console.log(`Hello, ${person.name}! You are ${person.age} years old.`);
}
const person1 = { name: 'John', age: 30 };
greet(person1);
在这个例子中,@typedef
定义了一个名为 Person
的类型别名,它是一个对象类型,包含 name
(字符串类型)和 age
(数字类型)两个属性。然后,函数 greet
接受一个参数 person
,其类型被注释为 Person
,表示这个参数应该是一个符合 Person
类型定义的对象。
@type
和 @typedef
的区别
@type
- 用于指定一个变量、参数或返回值的具体类型。它通常用于为已有的类型提供类型注释,而不是创建新的类型别名。
@typedef
- 用于定义一个新的类型别名。它允许你为一个复杂的类型结构创建一个更具描述性的名称,以便在代码中重复使用。
@param
和@returns
在 JSDoc 中,@param
用于描述函数的参数。
@param
语法:
@param {type} name - description
其中 type
是参数的类型,name
是参数的名称,description
是对参数的描述。
注意:@param
语法和@type
相同,但增加了一个参数名。 使用[]
可以把参数声明为可选的。
在 JSDoc 中,@returns
用于描述函数的返回值。
@returns
语法:
@returns {type} - description
其中 type
是函数的返回值类型,description
是对返回值的描述。
示例:
/**
* @param {number} a - The first number.
* @param {number} b - The second number.
* @param {number=} c - The third number.
* @param {number} [d=10] - The fourth number.
* @returns {number}
*/
function sum(a, b, c, d = 10) {
c = c ?? 0;
return a + b + c + d;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, undefined, undefined)); // 3
console.log(sum(1, 2, undefined, 10)); // 13
参数c
和d
被标记为可选参数。在调用函数时它们可能没有被传入值。因此,需要对参数进行类型判断。
@typedef
与 @param
@typedef
可以声明复杂类型。它和@param
在语法上有一定的相似性,但用途不同。
使用@typedef
定义一个包含多个属性的对象类型别名:
/**
* @typedef {Object} Person
* @property {string} name - The person's name.
* @property {number} age - The person's age.
* @property {string[]} hobbies - The person's hobbies.
*/
/**
* @param {Person} person - A person object.
*/
function displayPersonInfo(person) {
console.log(`Name: ${person.name}, Age: ${person.age}, Hobbies: ${person.hobbies.join(', ')}`);
}
@param
允许使用相似的语法。 注意,嵌套的属性名必须使用参数名做为前缀:
/**
* @param {Object} options - The shape is the same as SpecialType above
* @param {string} options.prop1
* @param {number} options.prop2
* @param {number=} options.prop3
* @param {number} [options.prop4]
* @param {number} [options.prop5=42]
*/
function special(options) {
let op5 = options.prop5 ?? 0;
return (options.prop4 || 1001) + op5;
}
使用@typedef
声明函数类型的别名:
/**
* @typedef {(a: number, b: number) => number} MathOperation
*/
/**
* @param {MathOperation} operation - A math operation function.
* @param {number} x - The first number.
* @param {number} y - The second number.
*/
function performMathOperation(operation, x, y) {
return operation(x, y);
}
在这个例子中,定义了一个名为MathOperation
的函数类型别名,代表接受两个数字参数并返回一个数字的函数类型。在函数performMathOperation
中使用了MathOperation
来注释参数operation
。
@param {MathOperation} operation - A math operation function.
:这个参数注释表明参数 operation
的类型是前面定义的 MathOperation
,即一个特定的函数类型。这意味着在调用这个函数时,传入的 operation
参数应该是一个接受两个数字参数并返回一个数字的函数。
@callback
在 JSDoc 中,@callback
用于定义回调函数的类型。
语法:
@callback callbackName - description
其中,callbackName
是回调函数的名称,用于在文档中引用这个回调函数类型。description
是对回调函数的描述。
在@callback
的注释块内部,可以使用@param
和@returns
等标签来描述回调函数的参数和返回值类型。
示例:
/**
* @callback ProcessFunction
* @param {string} data - The input data.
* @returns {number} The processed result.
*/
/**
* Processes data using a callback function.
* @param {string} inputData - The data to process.
* @param {ProcessFunction} callback - The callback function to process the data.
*/
function processData(inputData, callback) {
const result = callback(inputData);
return result;
}
在这个例子中:
- 使用
@callback
定义了一个名为ProcessFunction
的回调函数类型,它接受一个字符串类型的参数data
,并返回一个数字类型的值。 - 在函数
processData
的文档注释中,使用这个回调函数类型来注释参数callback
,表示这个参数应该是一个符合ProcessFunction
类型定义的回调函数。
@template
在 JSDoc 中,@template
用于描述函数或类的模板参数。
语法:
@template {typeParam} name - description
其中,typeParam
是模板参数的类型,name
是模板参数的名称,description
是对模板参数的描述。
使用@template
声明泛型:
/**
* @template T
* @param {T} x - A generic parameter that flows through to the return type
* @return {T}
*/
function someArg(x){ return x }
console.log(someArg(5)); // 5
console.log(someArg('hi')); // 'hi'
const obj = { name: "John", age: 30 };
console.log(someArg(obj)); // { name: "John", age: 30 }
使用 @template
声明了一个模板参数 T
。
@param {T} x
表明函数接受一个参数 x
,其类型为模板参数 T
。
@return {T}
明确了函数的返回值类型也是模板参数 T
。
函数换入的参数类型与返回值的类型保持一致。
用逗号或多个标记来声明多个类型参数:
/**
* @template T,U,V
* @template W,X
*/
可以在参数名前指定类型约束。
例如,对第一个参数的类型进行严格的限制:
/**
* @template {string} K - K must be a string or string literal
* @template {{ serious(): string }} Seriousalizable - must have a serious method
* @param {K} key
* @param {Seriousalizable} object
*/
function seriousalize(key, object) {
// ????
}
@template {string} K
定义了一个模板参数 K
,并限制其类型为字符串或字符串字面量。
也就是说,在调用函数seriousalize
时,第一个参数 key
的类型必须是字符串或字符串字面量。
一个综合示例:
/**
* @template T
* @function mapArray
* @param {T[]} array - The input array.
* @param {(item: T) => T} callback - The callback function to apply to each item.
* @returns {T[]} The mapped array.
*/
function mapArray(array, callback) {
return array.map(callback);
}
const numbers = [1, 2, 3];
const doubledNumbers = mapArray(numbers, (num) => num * 2);
console.log(doubledNumbers); // [2, 4, 6]
const strings = ['a', 'b', 'c'];
const upperCaseStrings = mapArray(strings, (str) => str.toUpperCase());
console.log(upperCaseStrings); // ['A', 'B', 'C']
在这个例子中,@template T
定义了一个模板参数T
,表示函数mapArray
可以接受任何类型的数组,并对数组中的每个元素应用一个回调函数。函数mapArray
的参数array
被注释为T[]
,表示一个类型为T
的数组,参数callback
被注释为(item: T) => T
,表示一个接受类型为T
的参数并返回类型为T
的函数。
@class
(or @constructor
)
在 JSDoc 中,@constructor
用于标记一个函数为构造函数。
语法:
@constructor通常放在函数的文档注释中,紧跟在函数的描述之后。
示例:
/**
* A simple class representing a person.
* @constructor
* @param {string} name - The person's name.
* @param {number} age - The person's age.
*/
function Person(name, age) {
this.name = name;
this.age = age;
}
const person1 = new Person('John', 30);
console.log(person1.name);
console.log(person1.age);
在这个例子中,@constructor
标记了函数Person
为构造函数。当使用new
关键字调用Person
函数时,它会创建一个新的对象,并将传入的参数赋值给对象的属性。
使用@constructor,
可以明确一个函数是用于创建对象的构造函数。
不幸的是,这意味着那些既能构造也能直接调用的构造函数不能使用@constructor
。
@this
编译器通常可以通过上下文来推断出this
的类型。
在 JSDoc 中,@this
用于描述函数中 this
的类型。
语法:
@this {type} - description
其中,type
是 this
的类型,description
是对 this
的描述。
明确this
指向“上下文对象”:
/**
* @this {Object} context - The context object.
* @param {number} a - The first number.
* @param {number} b - The second number.
* @returns {number} The sum of the two numbers.
*/
function sum(a, b) {
return this.a + this.b;
}
const context = { a: 5, b: 3 };
const result = sum.call(context, context.a, context.b);
console.log(result); // 8
在这个例子中,@this {Object} context - The context object.
表示在函数 sum
中,this
的类型是一个对象,描述为“上下文对象”。通过 sum.call(context, context.a, context.b)
调用函数 sum
,并将 context
作为 this
的值传入,从而可以在函数内部使用 this.a
和 this.b
来访问 context
对象的属性。
明确this
指向HTML 元素对象:
/**
* @this {HTMLElement}
* @param {*} e
*/
function callbackForLater(e) {
this.style.height = parseInt(e) + 'px';
}
@this {HTMLElement}
:
这个注释表明在函数 callbackForLater
中,this
的类型被期望为 HTMLElement
。当这个函数被调用时,this
应该指向一个 HTML 元素对象。
@extends
(or @augments
)
在 JSDoc 中,@extends
用于表示一个类型继承自另一个类型。
语法:
@extends {type} - description
其中,type
是被继承的类型,description
是对继承关系的描述。
当JavaScript类继承了一个基类,无处指定类型参数的类型。而@extends标记提供了这样一种方式:
/**
* @template T
* @extends {Set<T>}
*/
class SortableSet extends Set {
// ...
}
注意@extends
只作用于类。当前,无法实现构造函数继承类的情况。
@enum
在 JSDoc 中,@enum
用于定义枚举类型。
语法:
@enum {Object} name - description
其中,name
是枚举的名称,description
是对枚举的描述。在注释块内部,可以使用键值对的形式来定义枚举的成员。
@enum
标记允许创建一个对象字面量,它的成员都有确定的类型。与JavaScript里大多数的对象字面量不同:它不允许添加额外成员。
/** @enum {number} */
const JSDocState = {
BeginningOfLine: 0,
SawAsterisk: 1,
SavingComments: 2,
}
注意JSDoc 的@enum
与TypeScript的@enum
大不相同,JSDoc 的@enum
可以是任何类型。这意味着可以根据需要定义枚举成员的值为数字、字符串或其他复杂的数据类型。
例如,可以定义一个枚举,其中成员的值是对象或函数:
/**
* @enum {Object} MyEnum
* @property {function(): void} Action1 - Description of Action1.
* @property {function(): void} Action2 - Description of Action2.
*/
不支持的写法
对象字面量中标记属性可选是不能使用=
后缀的:
// 错误示例
const obj = {
prop1: 'value1',
prop2: 'value2',
prop3=: 'value3' // 错误的语法,不能用 =: 来表示可选属性
};
// 正确的写法是使用 ? 表示可选属性
const obj = {
prop1: 'value1',
prop2: 'value2',
prop3?: 'value3' // 正确的语法,表示 prop3 是可选属性
};
Nullable
类型只在启用了strictNullChecks
检查时才起作用:
/**
* @type {?number}
* With strictNullChecks: true -- number | null
* With strictNullChecks: off -- number
*/
var nullable;
@type {?number}
描述变量 nullable
的类型可以是number
或者 null
。
Non-nullable
类型没有意义,以其原类型对待:
/**
* @type {!number}
* Just has type number
*/
var normal;