TypeScript(一)类型

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 中有两种方式表示数组,比较灵活:

  1. 在元素类型后面接上 [] ,表示由此类型元素组成的一个数组
// 元素为数字类型的数组
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'.
  1. 第二种方式是使用数组泛型,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 = {}))

自定义赋值

  1. 可以给枚举项手动赋值,未手动赋值的枚举项会接着上一个枚举项递增。
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. 手动赋值的枚举项也可以为小数或负数,此时后续未手动赋值的项的递增步长仍为 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. 类型断言

有时候我们比编译器更清楚某一个值的类型,所以需要通过某种方式告诉编译器不进行特殊的数据检查和解构,手动的去指定一个值的类型(好比其它语言里的类型转换),这种方式称之为类型断言。

类型断言有两种方式:

  1. <类型>值
let str: string = 'sina'
let len: number = (<string>str).length
  1. 值 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 的区别:

  1. unknown 是 top type (任何类型都是它的 subtype)。任何类型的值都可以赋值给它,但它只能赋值给 unknown 和 any,因为只有它俩是 top type。

  2. 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 核心库声明文件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容