1. boolean
使用 boolean 定义布尔值类型:
let done: boolean = true
注意:使用构造函数创建的值不是 boolean 类型,而是 Boolean 对象
let bool: boolean = new Boolean(true)
// Type 'Boolean' is not assignable to type 'boolean'.
// 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.
直接调用 Boolean 创建的值是 boolean 类型
let bool: boolean = Boolean(false)
console.log(bool) // false
2. number
类似于 javascript ,typescript 支持十进制、十六进制、二进制、八进制字面量。
// 十进制
let num: number = 123
// 十六进制
let x: number = 0xf00d
console.log(x)
// 二进制
let y: number = 0b1010
console.log(y)
// 八进制
let z: number = 0o744
console.log(z)
编译结果:
var num = 123
var x = 0xf00d
console.log(x)
// 二进制
var y = 10
console.log(y)
// 八进制
var z = 484
console.log(z)
3. string
和 javascript 一样,可以使用双引号("")、单引号('')、反引号(``)表示字符串
let str: string = 'sina'
// 还可使用模板字符串
let fullname: string = 'mike'
let age: number = 12
let info = `the person info:
name is ${fullname};
age is ${age}`
console.log(info)
编译结果:
var fullname = 'mike'
var age = 12
var info = 'the person info:\n name is ' + fullname + ';\n age is ' + age
console.log(info)
4. Array
TypeScript 中有两种方式表示数组,比较灵活:
- 在元素类型后面接上
[]
,表示由此类型元素组成的一个数组
// 元素为数字类型的数组
let arr: number[] = [2, 3, 4]
编译结果:
var arr = [2, 3, 4]
- 数组中不允许出现指定类型之外的其他类型:
let nums: number[] = [1, '1', 2, 3, 5]
// Type 'string' is not assignable to type 'number'
- 数组个别方法的参数也会根据数组在定义时约定的类型进行限制:
let nums: number[] = [1, 1, 2, 3, 5]
nums.push('8')
// Argument of type '"8"' is not assignable to parameter of type 'number'.
- 第二种方式是使用数组泛型,Array<元素类型>
// 元素为字符串的数组
let array: Array<string>
array = ['sina', 'mike']
console.log(array)
编译结果:
var array
array = ['sina', 'mike']
console.log(array)
- 如果数组元素既可以是 number 又可以是 string 类型,可以通过以下方式:
let arr: (string | number)[] = [1, 4, 'sina']
console.log(arr)
注意:指示类型的圆括号()不可以省略,如果省略则会被编译器认为数组元素是其中一种类型
let arr: string | number[] = [3, 4, 5]
console.log(arr)
// let arr: string | number[] = [3, 4, 5, 'sina'] // 报错
- 还可以使用数组泛型表示元素为多种类型的数组
let arr: Array<string | number> = [3, 4, 'sina']
console.log(arr)
5. 元组 Tuple
元组可以理解成数组的扩展,表示一个已知长度和类型的数组,各元素的类型不必相同
- 定义一个第一项和第三项为 string 类型,第二项为数值长度为 3 的数组
let tuple: [string, number, string] = ['sina', 3, 'mike']
- 直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项
tuple = ['hello', 33, 'jack']
- 访问已知索引的元素:
let tuple: [string, number, string] = ['sina', 3, 'mike']
tuple[2].substr(2) // 'ke'
tuple[1].toFixed(2) // '3.00'
- 修改某一项的值:
let tuple: [string, number, string] = ['sina', 3, 'mike']
tuple[1] = 22
- 超出数组长度的元素称为越界元素:
const arr: [string, number] = ['sian', 4]
arr[2] = 2
// Tuple type '[string, number]' of length '2' has no element at index '2'.
- 通过数组方法修改或者添加元素时,所添加的元素必须是限制类型之一:
let arr: [string, number] = ['hello', 12]
arr.splice(1, 1, 'sina') // ['hello', 'sina']
arr.push(5) // ['hello', 12, 5]
arr.push(true) // 报错:Argument of type 'true' is not assignable to parameter of type 'string | number'.
6. 枚举 Enum
枚举类型用于取值被限定在一定范围内的场景。比如某个值的状态由数字表示,每个数字对应一种状态,但是我们得记忆或认知是对语义化的东西敏感,记不住那些数字,于是可以通过枚举表示。
枚举使用 enum
关键字定义:
enum Status {
pending,
resolve,
reject
}
枚举成员的序列号默认被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射:
console.log(Status.resolve) // 1
console.log(Status[2]) // 'reject'
编译结果:
var Status
;(function (Status) {
Status[(Status['pending'] = 0)] = 'pending'
Status[(Status['resolve'] = 1)] = 'resolve'
Status[(Status['reject'] = 2)] = 'reject'
})(Status || (Status = {}))
自定义赋值
- 可以给枚举项手动赋值,未手动赋值的枚举项会接着上一个枚举项递增。
enum Days {
Sun = 7,
Mon = 1,
Tue,
Wed,
Thu,
Fri,
Sat
}
console.log(Days.Sun) // 7
console.log(Days.Mon) // 1
console.log(Days.Tue) // 2
console.log(Days.Sat) // 6
如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 并不会察觉到这一点的:
enum Colors {
green = 3,
red = 1,
blue,
orange,
pink,
white
}
console.log(Days.orange) // 3
console.log(Days.green) // 3
console.log(Days[3]) // 'orange'
console.log(Days[3]) // 'orange'
以上枚举项递增到 orange 时重复了,但是 TypeScript 并没有报错,导致 Colors[3]
的值开始是 green
,之后被 orange
重写了。
编译结果:
var Colors
;(function (Colors) {
Colors[(Colors['green'] = 3)] = 'green'
Colors[(Colors['red'] = 1)] = 'red'
Colors[(Colors['blue'] = 2)] = 'blue'
Colors[(Colors['orange'] = 3)] = 'orange'
Colors[(Colors['pink'] = 4)] = 'pink'
Colors[(Colors['white'] = 5)] = 'white'
})(Colors || (Colors = {}))
所以使用的时候一定注意,不要出现覆盖的情况。
- 手动赋值的枚举项也可以为小数或负数,此时后续未手动赋值的项的递增步长仍为 1:
enum Colors {
green = 3,
red = 1.5,
blue,
orange,
pink,
white
}
console.log(Days.orange) // 3
console.log(Days.green) // 3.5
console.log(Days[3.5]) // 'orange'
console.log(Days[3.5]) // 'orange'
7. Void
javasctipt 中如果函数没有返回值,默认返回 undefined。
void 表示没有任何类型,在 TypeScript 用 void 表示一个没有返回值的函数。
function foo(): void {
console.log('hello void')
}
函数执行后仍然返回 undefined
。
为什么指定 void 类型,返回仍然是 undefined?
因为默认情况下 null 和 undefined 是所有类型的子类型(包括 void)。也就是说可以把 null 和 undefined 赋值给 number 等其他类型的变量。(前提是 strictNullChecks
为关闭状态)。
事实上声明一个 void 类型的变量没有什么用,因为只能将它赋值为 undefined 或 null( strictNullChecks
是关闭状态)
let unusable: void = undefined
unusable = null
// 如果strictNullChecks为开启状态下则会报错: Type 'null' is not assignable to type 'void'.
如果函数参数未指定类型,则隐式具有“any”类型。
const baz = (text): void => {
console.log(text)
}
// Parameter 'text' implicitly has an 'any' type.
8. Null & Undefined
let u: undefined = undefined
let n: null = null
默认情况下,null 和 undefined 是所有类型的子集(包括 void),可以把他们赋值给这些类型的变量。
但是如果开启了 strictNullChecks
标记,null 和 undefined 只能赋值给 void 和它们自己。
9. Never
never 类型表示永不存在的值的类型。比如那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
function error(): never {
throw new Error('Oops...)
}
let neverVar = (): never => { while (true) { } }
never 类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)。 即使 any 也不可以赋值给 never。
重点: never 与其他类型的联合后,是没有 never 的
type ET = string | number | never
// ET 实际上只有 string 和 number两种类型
基于 never 的特性,还可以使用 never 实现一些有意思的功能。比如可以把 never 作为接口类型下的属性类型,用来禁止写接口下特定的属性,如下:
const props: {
id: number,
name?: never
} = {
id: 1
}
props.name = null; // ts(2322))
props.name = 'str'; // ts(2322)
props.name = 1; // ts(2322)
此时,无论我们给 props.name 赋什么类型的值,它都会提示类型错误,实际效果等同于 name 只读
10. 联合类型
联合类型表示取值可以为多种类型中的一种,使用 |
分隔每个类型。
let foo: string | number
foo = 'sina'
foo = 33
11. 类型断言
有时候我们比编译器更清楚某一个值的类型,所以需要通过某种方式告诉编译器不进行特殊的数据检查和解构,手动的去指定一个值的类型(好比其它语言里的类型转换),这种方式称之为类型断言。
类型断言有两种方式:
- <类型>值
let str: string = 'sina'
let len: number = (<string>str).length
- 值 as 类型
let str: stirng = 'sina'
let len: number = (str as string).length
注意:在 tsx 语法中必须使用 as
,否则 tsx 会把方式当作标签去解析。
实例:
当 TypeScript 不确定一个变量到底是哪个类型的时候,只能访问此变量联合类型里共有的属性或方法,但有时我们确实需要在还不确定类型的时候就访问其中一个类型的属性或方法,如下:
function getLength(str: string | number): number {
if (str.length || str.length === 0) {
return str.length
}
return str.toString().length
}
// Property 'length' does not exist on type 'string | number'. Property 'length' does not exist on type 'number'.
以上代码本身没有问题,在逻辑里都做了兼容性判断,但是 ts 在编译时会去判断 str 的联合类型是否都存在 length 属性,编译结果是 str 为 number 时不存在 length 属性,所以会抛出错误。
此时就可以使用类型断言将参数 str 断言成 string 类型。如下:
function getLength(str: string | number): number {
if ((<string>str).length || (str as string).length === 0) {
return (str as string).length
}
return str.toString().length
}
注意:类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的:
function getLength(str: string | number): number {
return (str as boolean).length
}
// Property 'length' does not exist on type 'boolean'. return (str as boolean).length
12. 任意值 Any
any 会跳过类型检查器对值的检查,任何值都可以赋值给 any 类型,它通常被称为 top type。
任意类型的值可以赋值给 any 类型的变量,any 类型的值也可以赋值给任意类型的的变量。
比如:
let num: any
num = 3
num = 'str'
let count: number = 4
count = 3
count = num
任意值类型
- 一个普通类型的值,在赋值过程中是不允许改变类型的:
let str: string = 'sina'
str = 7
// Type '7' is not assignable to type 'string'.
- 如果是 any 类型,则允许被赋值为任意类型:
let foo: any = 'sina'
foo = 12
foo = true
console.log(foo)
- 如果用 const 声明必须有初始值,和 js 是一致的:
const bar: any = 'sina'
console.log(bar)
- 声明一个元素为任意类型的数组:
const arrAny: any[] = [3, 'sian', true, [3]]
console.log(arrAny)
- 长度仍然不可越界:
const arrAny1: [string, any] = ['sina', 4]
console.log(arrAny1)
任意值的属性和方法
- 任意值上允许访问任何属性:
let str: any = 'hello'
console.log(str.foo) // undefined
// 编译器并不会报错,转成js后运行报错:Cannot read property 'bar' of undefined
console.log(str.foo.bar)
- 也允许调用任何方法
let str: any = 'sina'
str.getName()
未声明类型的变量
变量如果在声明时未指定类型,则会被识别为任意值类型
let str
str = 'sina'
str = 7
str.foo('Tom')
等价于:
let str: any
str = 'sina'
str = 7
str.foo()
13. 未知类型 unknown
表示未知类型的值。any 一样,所有类型都可以分配给 unknown,实际上是类型安全的 any。
和 any 的区别:
unknown 是 top type (任何类型都是它的 subtype)。任何类型的值都可以赋值给它,但它只能赋值给 unknown 和 any,因为只有它俩是 top type。
any 既是 top type, 又是 bottom type (它是任何类型的 subtype ),所以任何类型的值可以赋值给 any,同时 any 类型的值也可以赋值给任何类型。 这导致 any 基本上就是放弃了任何类型检查。
unknown 如果要赋值给其他类型变量,可以先判断类型在赋值。比如:
使用 typeof
let foo: string
let count: unknown
count = 'hello'
count = true
if (typeof count === 'string') {
foo = count
}
或者使用类型断言:
foo = count as string
// 另一种写法
foo = <string>count
14. 类型推论
如果没有明确指定类型,那么 TypeScript 会依照类型推论的规则推断出一个类型。
let str = 'sina'
str = 3 // Error
等价于:
let str: string = 'sina'
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而不会被被 ts 进行类型检查:
let str
str = 'sina'
str = 3
15. 交叉类型
交叉类型是将多个类型合并为一个类型。 把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性,使用 &
定义交叉类型。
interface A {
name: string
age: number
}
interface B {
name: string
gender: string
}
let info: A & B = {
name: 'jack',
age: 23,
gender: 'male'
}
info 既是 A 类型,同时也是 B 类型。
注意:交叉类型取的是多个类型的并集,如果出现 key 相同但是类型不同,则该 key 为 never 类型。
16. 类型别名
就是给类型起一个别名
语法:
type 别名 = 类型
type num = string
let count: num = 'str'
type numX = 1 | 2 | 3 | 4
let countX: numX = 3
使用 typeof
关键字把值反推类型
const obj = {
name: 'jack',
age: 34
}
type User = typeof obj
那么 User 的类型就是
type User = {
name: string
age: number
}
注意:类型别名不能被 extends 和 implements
type 可以通过交叉类型实现继承。
type A = B & C & D
17. 内置对象
ECMAScript 提供的内置对象有:String、Number、Boolean、Error、Date、RegExp 等。
const num: Number = new Number(11)
const str: String = new String('hello')
const bool: Boolean = new Boolean(true)
const reg: RegExp = /[0-9]/
const date: Date = new Date()
const err: Error = new Error('oops...')
DOM 和 BOM 内置对象有: Document、HTMLElement、HTMLCollection、NodeList、MouseEvent 等。
const html: HTMLElement = document.documentElement
const spans: NodeList = document.querySelectorAll('span')
const lis: HTMLCollection = document.getElementsByTagName('li')
document.addEventListener('click', function (e: MouseEvent) {
// do...
})
类数组对象:
function foo() {
const args: IArguments = arguments
}
更多查看 ts 核心库声明文件