LayaBox---TypeScript---JavaScript文件类型检查
Posted 格拉格拉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LayaBox---TypeScript---JavaScript文件类型检查相关的知识,希望对你有一定的参考价值。
目录
8.null,undefined,和空数组的类型是any或any[]
12.5 @typedef、@callback 和 @param
1.介绍
TypeScript 2.3以后的版本支持使用--checkJs
对.js
文件进行类型检查和错误提示。
可以通过添加// @ts-nocheck
注释来忽略类型检查;相反,你可以通过去掉--checkJs
设置并添加一个// @ts-check
注释来选则检查某些.js
文件。 你还可以使用// @ts-ignore
来忽略本行的错误。 如果你使用了tsconfig.json
,JS检查将遵照一些严格检查标记,如noImplicitAny
,strictNullChecks
等。 但因为JS检查是相对宽松的,在使用严格标记时可能会有些出乎意料的情况。
对比.js
文件和.ts
文件在类型检查上的差异,有如下几点需要注意:
2.用JSDoc类型表示类型信息
.js
文件里,类型可以和在.ts
文件里一样被推断出来。 同样地,当类型不能被推断时,它们可以通过JSDoc来指定,就好比在.ts
文件里那样。 如同TypeScript,--noImplicitAny
会在编译器无法推断类型的位置报错。
JSDoc注解修饰的声明会被设置为这个声明的类型。比如:
/** @type number */
var x;
x = 0; // OK
x = false; // Error: boolean is not assignable to number
你可以在这里找到所有JSDoc支持的模式,JSDoc文档。
3.属性的推断来自于类内的赋值语句
在.js
文件里,编译器从类内部的属性赋值语句来推断属性类型。 属性的类型是在构造函数里赋的值的类型,除非它没在构造函数里定义或者在构造函数里是undefined
或null
。 若是这种情况,类型将会是所有赋的值的类型的联合类型。 在构造函数里定义的属性会被认为是一直存在的,然而那些在方法,存取器里定义的属性被当成可选的。
class C
constructor()
this.constructorOnly = 0
this.constructorUnknown = undefined
method()
this.constructorOnly = false // error, constructorOnly is a number
this.constructorUnknown = "plunkbat" // ok, constructorUnknown is string | undefined
this.methodOnly = 'ok' // ok, but y could also be undefined
method2()
this.methodOnly = true // also, ok, y's type is string | boolean | undefined
如果一个属性从没在类内设置过,它们会被当成未知的。
如果类的属性只是读取用的,那么就在构造函数里用JSDoc声明它的类型。 如果它稍后会被初始化,你甚至都不需要在构造函数里给它赋值:
class C
constructor()
/** @type number | undefined */
this.prop = undefined;
/** @type number | undefined */
this.count;
let c = new C();
c.prop = 0; // OK
// Error: string is not assignable to number|undefined
c.count = "string";
4.构造函数等同于类
ES2015以前,javascript使用构造函数代替类。 编译器支持这种模式并能够将构造函数识别为ES2015的类。 属性类型推断机制和上面介绍的一致。
function C()
this.constructorOnly = 0
this.constructorUnknown = undefined
C.prototype.method = function()
this.constructorOnly = false // error
this.constructorUnknown = "plunkbat" // OK, the type is string | undefined
5.支持CommonJs模块
在.js
文件里,TypeScript能识别出CommonJS模块。 对exports
和module.exports
的赋值被识别为导出声明。 相似地,require
函数调用被识别为模块导入。
例如:
// same as `import module "fs"`
const fs = require("fs");
// same as `export function readFile`
module.exports.readFile = function(f)
return fs.readFileSync(f);
6.类、函数和对象字面量是命名空间
.js
文件里的类是命名空间。 它可以用于嵌套类。
比如:
class C
C.D = class
ES2015之前的代码,它可以用来模拟静态方法:
function Outer()
this.y = 2
Outer.Inner = function()
this.yy = 2
它还可以用于创建简单的命名空间:
var ns =
ns.C = class
ns.func = function()
同时还支持其它的变化:
// 立即调用的函数表达式
var ns = (function (n)
return n || ;
)();
ns.CONST = 1
// defaulting to global
var assign = assign || function()
// code goes here
assign.extra = 1
7.对象字面量是开放的
.ts
文件里,用对象字面量初始化一个变量的同时也给它声明了类型。 新的成员不能再被添加到对象字面量中。 这个规则在.js
文件里被放宽了;对象字面量具有开放的类型,允许添加并访问原先没有定义的属性。例如:
var obj = a: 1 ;
obj.b = 2; // Allowed
对象字面量的表现就好比具有一个默认的索引签名[x:string]: any
,它们可以被当成开放的映射而不是封闭的对象。
与其它JS检查行为相似,这种行为可以通过指定JSDoc类型来改变,例如:
/** @type a: number */
var obj = a: 1 ;
obj.b = 2; // Error, type a: number does not have property b
8.null,undefined,和空数组的类型是any或any[]
任何用
null
,undefined
初始化的变量,参数或属性,它们的类型是any
。 任何用[]
初始化的变量,参数或属性,它们的类型是any[]
。
唯一的例外是像上面那样有多个初始化器的属性。
function Foo(i = null)
if (!i) i = 1;
var j = undefined;
j = 2;
this.l = [];
var foo = new Foo();
foo.l.push(foo.i);
foo.l.push("end");
9.函数参数是默认可选的
由于在ES2015之前无法指定可选参数,因此.js
文件里所有函数参数都被当做是可选的。 使用比预期少的参数调用函数是允许的。需要注意的一点是,使用过多的参数调用函数会得到一个错误。
例如:
function bar(a, b)
console.log(a + " " + b);
bar(1); // OK, second argument considered optional
bar(1, 2);
bar(1, 2, 3); // Error, too many arguments
使用JSDoc注解的函数会被从这条规则里移除。 使用JSDoc可选参数语法来表示可选性。
比如:
/**
* @param string [somebody] - Somebody's name.
*/
function sayHello(somebody)
if (!somebody)
somebody = 'John Doe';
console.log('Hello ' + somebody);
sayHello();
10.由arguments推断出的var-args参数声明
如果一个函数的函数体内有对arguments
的引用,那么这个函数会隐式地被认为具有一个var-arg参数(比如:(...arg: any[]) => any
))。使用JSDoc的var-arg语法来指定arguments
的类型。
/** @param ...number args */
function sum(/* numbers */)
var total = 0
for (var i = 0; i < arguments.length; i++)
total += arguments[i]
return total
11.未指定的类型参数默认为any
11.1在extends语句中:
React.Component
被定义成具有两个类型参数,Props
和State
。 在一个.js
文件里,没有一个合法的方式在extends语句里指定它们。默认地参数类型为any
:
import Component from "react";
class MyComponent extends Component
render()
this.props.b; // Allowed, since this.props is of type any
使用JSDoc的@augments
来明确地指定类型。
例如:
import Component from "react";
/**
* @augments Component<a: number, State>
*/
class MyComponent extends Component
render()
this.props.b; // Error: b does not exist on a:number
11.2在JSDoc引用中:
JSDoc里未指定的类型参数默认为any
:
/** @typeArray */
var x = [];
x.push(1); // OK
x.push("string"); // OK, x is of type Array<any>
/** @typeArray.<number> */
var y = [];
y.push(1); // OK
// Error, string is not assignable to number
y.push("string");
11.3在函数调用中
泛型函数的调用使用arguments
来推断泛型参数。有时候,这个流程不能够推断出类型,大多是因为缺少推断的源;在这种情况下,类型参数类型默认为any
。例如:
var p = new Promise((resolve, reject) => reject() );
p; // Promise<any>;
12.支持的JSDoc
下面的列表列出了当前所支持的JSDoc注解,你可以用它们在JavaScript文件里添加类型信息。
注意,没有在下面列出的标记(例如
@async
)都是还不支持的。
@type
@param
(or@arg
or@argument
)@returns
(or@return
)@typedef
@callback
@template
@class
(or@constructor
)@this
@extends
(or@augments
)@enum
它们代表的意义与usejsdoc.org上面给出的通常是一致的或者是它的超集。 下面的代码描述了它们的区别并给出了一些示例。
12.1 @type
可以使用@type
标记并引用一个类型名称(原始类型,TypeScript里声明的类型,或在JSDoc里@typedef
标记指定的) 可以使用任何TypeScript类型和大多数JSDoc类型。
/**
* @type string
*/
var s;
/** @type Window */
var win;
/** @type PromiseLike<string> */
var promisedString;
// You can specify an html Element with DOM properties
/** @type HTMLElement */
var myElement = document.querySelector(selector);
element.dataset.myData = '';
@type
可以指定联合类型—例如,string
和boolean
类型的联合。
/**
* @type (string | boolean)
*/
var sb;
注意,括号是可选的。
/**
* @type string | boolean
*/
var sb;
有多种方式来指定数组类型:
/** @type number[] */
var ns;
/** @type Array.<number> */
var nds;
/** @type Array<number> */
var nas;
还可以指定对象字面量类型。 例如,一个带有a
(字符串)和b
(数字)属性的对象:
/** @type a: string, b: number */
var var9;
可以使用字符串和数字索引签名来指定map-like
和array-like
的对象,使用标准的JSDoc语法或者TypeScript语法。
/**
* A map-like object that maps arbitrary `string` properties to `number`s.
*
* @type Object.<string, number>
*/
var stringToNumber;
/** @type Object.<number, object> */
var arrayLike;
这两个类型与TypeScript里的 [x: string]: number
和 [x: number]: any
是等同的。
可以使用TypeScript或Closure语法指定函数类型。
/** @type function(string, boolean): number Closure syntax */
var sbn;
/** @type (s: string, b: boolean) => number Typescript syntax */
var sbn2;
或者直接使用未指定的Function
类型:
/** @type Function */
var fn7;
/** @type function */
var fn6;
Closure的其它类型也可以使用:
/**
* @type * - can be 'any' type
*/
var star;
/**
* @type ? - unknown type (same as 'any')
*/
var question;
12.2转换
TypeScript借鉴了Closure里的转换语法。 在括号表达式前面使用@type
标记,可以将一种类型转换成另一种类型
/**
* @type number | string
*/
var numberOrString = Math.random() < 0.5 ? "hello" : 100;
var typeAssertedNumber = /** @type number */ (numberOrString)
12.3导入类型
可以使用导入类型从其它文件中导入声明。 这个语法是TypeScript特有的,与JSDoc标准不同:
/**
* @param p import("./a").Pet
*/
function walk(p)
console.log(`Walking $p.name...`);
导入类型也可以使用在类型别名声明中:
/**
* @typedef Pet import("./a").Pet
*/
/**
* @type Pet
*/
var myPet;
myPet.name;
导入类型可以用在从模块中得到一个值的类型。
/**
* @type typeof import("./a").x
*/
var x = require("./a").x;
12.4 @param 和 @returns
@param
语法和@type
相同,但增加了一个参数名。 使用[]
可以把参数声明为可选的:
// Parameters may be declared in a variety of syntactic forms
/**
* @param string p1 - A string param.
* @param string= p2 - An optional param (Closure syntax)
* @param string [p3] - Another optional param (JSDoc syntax).
* @param string [p4="test"] - An optional param with a default value
* @return string This is the result
*/
function stringsStringStrings(p1, p2, p3, p4)
// TODO
函数的返回值类型也是类似的:
/**
* @return PromiseLike<string>
*/
function ps()
/**
* @returns a: string, b: number - May use '@returns' as well as '@return'
*/
function ab()
12.5 @typedef、@callback 和 @param
@typedef
可以用来声明复杂类型。 和@param
类似的语法。
/**
* @typedef Object SpecialType - creates a new type named 'SpecialType'
* @property string prop1 - a string property of SpecialType
* @property number prop2 - a number property of SpecialType
* @property number= prop3 - an optional number property of SpecialType
* @prop number [prop4] - an optional number property of SpecialType
* @prop number [prop5=42] - an optional number property of SpecialType with default
*/
/** @type SpecialType */
var specialTypeObject;
可以在第一行上使用object
或Object
。
/**
* @typedef object SpecialType1 - creates a new type named 'SpecialType'
* @property string prop1 - a string property of SpecialType
* @property number prop2 - a number property of SpecialType
* @property number= prop3 - an optional number property of SpecialType
*/
/** @type SpecialType1 */
var specialTypeObject1;
@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)
return (options.prop4 || 1001) + options.prop5;
@callback
与@typedef
相似,但它指定函数类型而不是对象类型:
/**
* @callback Predicate
* @param string data
* @param number [index]
* @returns boolean
*/
/** @type Predicate */
const ok = s => !(s.length % 2);
当然,所有这些类型都可以使用TypeScript的语法@typedef
在一行上声明:
/** @typedef prop1: string, prop2: string, prop3?: number SpecialType */
/** @typedef (data: string, index?: number) => boolean Predicate */
12.6 @template
使用@template
声明泛型:
/**
* @template T
* @param T p1 - A generic parameter that flows through to the return type
* @return T
*/
function id(x) return x
用逗号或多个标记来声明多个类型参数:
/**
* @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)
// ????
12.7 @constuctor
编译器通过this
属性的赋值来推断构造函数,但你可以让检查更严格提示更友好,你可以添加一个@constructor
标记:
/**
* @constructor
* @param number data
*/
function C(data)
this.size = 0;
this.initialize(data); // Should error, initializer expects a string
/**
* @param string s
*/
C.prototype.initialize = function (s)
this.size = s.length
var c = new C(0);
var result = C(1); // C should only be called with new
通过
@constructor
,this
将在构造函数C
里被检查,因此你在initialize
方法里得到一个提示,如果你传入一个数字你还将得到一个错误提示。如果你直接调用C
而不是构造它,也会得到一个错误。这意味着那些既能构造也能直接调用的构造函数不能使用@constructor
。
12.8 @this
编译器通常可以通过上下文来推断出this
的类型。但你可以使用@this
来明确指定它的类型:
/**
* @this HTMLElement
* @param * e
*/
function callbackForLater(e)
this.clientHeight = parseInt(e) // should be fine!
12.9 @extends
当JavaScript类继承了一个基类,无处指定类型参数的类型。而@extends
标记提供了这样一种方式:
/**
* @template T
* @extends Set<T>
*/
class SortableSet extends Set
// ...
⚠️注意:@extends
只作用于类。当前,无法实现构造函数继承类的情况。
12.10 @enum
@enum
标记允许你创建一个对象字面量,它的成员都有确定的类型。不同于JavaScript里大多数的对象字面量,它不允许添加额外成员。
/** @enum number */
const JSDocState =
BeginningOfLine: 0,
SawAsterisk: 1,
SavingComments: 2,
注意@enum
与TypeScript的@enum
大不相同,它更加简单。然而,不同于TypeScript的枚举,@enum
可以是任何类型:
/** @enum function(number): number */
const Math =
add1: n => n + 1,
id: n => -n,
sub1: n => n - 1,
12.11 更多示例
var someObj =
/**
* @param string param1 - Docs on property assignments work
*/
x: function(param1)
;
/**
* As do docs on variable assignments
* @return Window
*/
let someFunc = function();
/**
* And class methods
* @param string greeting The greeting to use
*/
Foo.prototype.sayHi = (greeting) => console.log("Hi!");
/**
* And arrow functions expressions
* @param number x - A multiplier
*/
let myArrow = x => x * x;
/**
* Which means it works for stateless function components in JSX too
* @param a: string, b: number test - Some param
*/
var sfc = (test) => <div>test.a.charAt(0)</div>;
/**
* A parameter can be a class constructor, using Closure syntax.
*
* @param new(...args: any[]): object C - The class to register
*/
function registerClass(C)
/**
* @param ...string p1 - A 'rest' arg (array) of strings. (treated as 'any')
*/
function fn10(p1)
/**
* @param ...string p1 - A 'rest' arg (array) of strings. (treated as 'any')
*/
function fn9(p1)
return p1.join();
12.12 已不支持的模式
1.在值空间中将对象视为类型是不可以的,除非对象创建了类型,如构造函数。
function aNormalFunction()
/**
* @type aNormalFunction
*/
var wrong;
/**
* Use 'typeof' instead:
* @type typeof aNormalFunction
*/
var right;
2.对象字面量属性上的=
后缀不能指定这个属性是可选的:
/**
* @type a: string, b: number=
*/
var wrong;
/**
* Use postfix question on the property name instead:
* @type a: string, b?: number
*/
var right;
3.Nullable
类型只在启用了strictNullChecks
检查时才启作用:
/**
* @type ?number
* With strictNullChecks: true -- number | null
* With strictNullChecks: off -- number
*/
var nullable;
4.Non-nullable
类型没有意义,以其原类型对待:
/**
* @type !number
* Just has type number
*/
var normal;
不同于JSDoc类型系统,TypeScript只允许将类型标记为包不包含
null
。 没有明确的Non-nullable
-- 如果启用了strictNullChecks
,那么number
是非null
的。 如果没有启用,那么number
是可以为null
的。
以上是关于LayaBox---TypeScript---JavaScript文件类型检查的主要内容,如果未能解决你的问题,请参考以下文章