JavaScript
数据类型
值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
引用数据类型:对象(Object)、数组(Array)、函数(Function)。
TypeScript
var是javascript中的一个关键字,用于定义变量、函数、对象等。JavaScript中所有的变量均可通过var关键字来定义。
TypeScript支持与JavaScript几乎相同的数据类型,多了个枚举类型enum。
定义变量格式:let [变量名称]: [数据类型],如let isDone: boolean = false;
元组 Tuple
我们知道数组中元素的数据类型都一般是相同的(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组。
元组中允许存储不同类型的元素,元组可以作为参数传递给函数。元组为方法返回多个值提供了便捷。C#新特性也有元组和解构概念。
创建元组的语法格式如下:
var tuple_name = [value1,value2,value3,…value n]
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string
和number
类型的元组。
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
当访问一个已知索引的元素,会得到正确的类型:
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
当访问一个越界的元素,会使用联合类型替代:
x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型
console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString
x[6] = true; // Error, 布尔不是(string | number)类型
联合类型是高级主题,我们会在以后的章节里讨论它。
解构元组
我们也可以把元组元素赋值给变量,如下所示:
var a =[10,"Runoob"]
var [b,c] = a
console.log( b )
console.log( c )
编译以上代码,得到以下 JavaScript 代码:
var a = [10, "Runoob"];
var b = a[0], c = a[1];
console.log(b);
console.log(c);
输出结果为:
10
Runoob
Any
Any编译阶段不检查类型,能够在它上面调用任意的方法。而object类型不能在它上面调用任意的方法。
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)
let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.
Null 和 Undefined
默认情况下null
和undefined
是所有类型的子类型。 就是说你可以把 null
和undefined
赋值给number
类型的变量。
然而,当你指定了--strictNullChecks
标记,null
和undefined
只能赋值给void
和它们各自。
Never
never
类型表示的是那些永不存在的值的类型。 例如, never
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never
类型,当它们被永不为真的类型保护所约束时。
never
类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never
的子类型或可以赋值给never
类型(除了never
本身之外)。 即使 any
也不可以赋值给never
。
never的主要作用就是充当Typescript类型系统里的Bottom Type (Typescript还有个top type unknown和即是top也是bottom的any)。
TS3.7起: 返回never的函数被纳入控制流分析, 之后的代码会 (像throw之后一样) 被判定为unreachable. 如: node中 process.exit()后.
还可以用来测试, 比如改一处类型看会引起哪些代码类型出错. (never类型对应一个空集, 你无法传入或使用一个 never类型的"值" )
这个用法扩展一下: 可以制造无害但人类必须involved的类型错误
比如我是个API的提供者,提供了一个 `foo(input: string): void` 的API,想要删掉
突然删掉不够友好,加个 @deprecated 又显然不能确保消费者会乖乖删掉
这时可以给foo加一个never参数: `foo(input: string, WILL_BE_REMOVED: never): void`
API仍然存在,行为也不变,只是强迫消费者要么改成别的API,要么调用时写成 `foo(input, 0 as never)`,不管怎么说保证有人看到了
再过个版本删掉就行
用来告知编译器某处代码永远不会执行,从而获得编译器提示, 以避免程序员在一个永远不会(或不应该)执行的地方编写代码.
Unreachable检查
例如,某萌新写下了自己的第一个程序如下(憋住别笑 ):
process.exit(0) console.log("hello world")
如果在js下,萌新可能没法一次跑过了. 但是如果ts下,该萌新可以获得一个编译器提示,被告知,
exit()
后面的代码是不可达的. ts编译器之所以可以「贴心的」给萌新提示,就是因为process.exit()
返回类型被定义为了 never,另外再给出几个常见的使用场景:
1.监听套接字
function listen() : never{ while(true){ let conn = server.accept() } } listen() console.log("!!!") //Error: Unreachable code detected.ts(7027)
2. 某个方法总是抛出异常
function throwError(msg: string ) :never { throw new Error(msg) } throwError("some error") console.log("!!!") //Error: Unreachable code detected.ts(7027)
收窄类型
除了上面列举的识别「Unreachable code」之外, 还可以帮助编译器收窄其他数据的类型.
例如下面的两个例子中,后者用never避免了一次空检查:
function throwError(){ throw new Error() } function firstChar(msg : string | undefined){ if(msg==undefined) throwError() let chr =msg.charAt(1) //❌Object is possibly 'undefined'.
function throwError() :never{ throw new Error() } function firstChar(msg : string | undefined){ if(msg==undefined) throwError() let chr =msg.charAt(1) // ✅
总结
1.检查「Unreachable code」
2.间接收窄其他数据的类型
类型断言
“类型断言更像是类型的选择,而不是类型转换”。类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式。 其一是“尖括号”语法:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
另一个为as
语法:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有 as
语法断言是被允许的。
双重断言
类型断言,尽管我们已经证明了它并不是那么安全,但它也还是有用武之地。如下一个非常实用的例子所示,当使用者了解传入参数更具体的类型时,类型断言能按预期工作:
function handler(event: Event) {
const mouseEvent = event as MouseEvent;
}
然而,如下例子中的代码将会报错,尽管使用者已经使用了类型断言:
function handler(event: Event) {
const element = event as HTMLElement; // Error: 'Event' 和 'HTMLElement' 中的任何一个都不能赋值给另外一个
}
如果你仍然想使用那个类型,你可以使用双重断言。首先断言成兼容所有类型的 any,编译器将不会报错:
function handler(event: Event) {
const element = (event as any) as HTMLElement; // ok
}
关于let
你可能已经注意到了,我们使用let
关键字来代替大家所熟悉的JavaScript关键字var
。 let
关键字是JavaScript的一个新概念,TypeScript实现了它。 我们会在以后详细介绍它,很多常见的问题都可以通过使用 let
来解决,所以尽可能地使用let
来代替var
吧。