tsconfig.json 配置文件
{
"compilerOptions": {
"target": "es5",
"lib": ["dom"],
"strict": true
},
"exclude": ["node_modules"]
}
八个基本类型
boolean
let bool: boolean = false
number
let num: number = 1
string
let str: string = 'Hi'
// 字符串字面量类型
let str: 'name'
str = 'other name' // error
array
// 1. 推荐写法
let arr: number[] = [1, 2, 3]
let arr2: number | string[] = ['1', 2, '3']
// 2. 第二种写法(tslint可能会警告需要使用第一种写法,可以在tslint.json的rules中配置"array-rules": [false]关闭警告)
let arr: Array<number> = [1, 2, 3]
let arr2: Array<number | string> = [1, 2, '3']
object
let obj: object = { name: 'hello' }
-
symbol -
null
let n: null = null
undefined
let u: undefined = undefined
注: 在没有配置 "strictNullChecks": true 规则时
null和undefined可以赋值给任意类型的值
六个补充类型
元组: 已知数组上每个位置上的数据类型
let tuple: [number, string, boolean]
tuple = [1, '2', true]
tuple[0] = 2
// TS2.6 版本之前, 含有越界元素(超出元组长度的元素)且越界元素是元组中指定的数据类型不会报错
// TS2.6 版本之后, 含有越界元素会报错
tuple = [1, '2', true, 3] // TS2.6前不会报错
tuple = [1, '2', true, null] // 都会报错, null不是元组指定类型中的任意一种
enum: 枚举
enum Status {
SUCCESS = 200,
NOT_FOUND = 404,
REDIRECT = 301
}
// 默认是从 0 开始编号
enum Users {
ZhangSan, // 0
LiSi, // 1
WangWu // 2
}
// 可以指定从多少开始编号
enum Users {
ZhangSan = 5, // 5
LiSi, // 6
WangWu // 7
}
-
any: any类型的数据可以赋值为任意类型 -
void: 函数无返回值
const alertMessage = (msg: string): void => {
window.alert(msg)
}
never: 永不存在值的类型
const throwError = (msg: string): never => {
throw Error(msg)
}
const infiniteFunc = (): never => {
while(true) {}
}
注:never 类型是任何类型的子类型,它可以赋值给任意类型;never 无子类型,除 never 类型外没有可以赋值给它的类型
unknown: unknown 相对于 any 是安全的,不像 any 类型的值是可以随便操作的
字面量类型
常量类型
- 字符串字面量类型
- 数字字面量类型
// 字符串字面量类型
let name: 'YM' = 'YM'
// name = 'LiSa' // 报错: 不能将类型“"LiSa"”分配给类型“"YM"”
// 数字字面量类型
let age: 80 = 80
// age = 46 // 报错: 不能将类型“46”分配给类型“80”
类型断言
有时候自己写代码的时候逻辑比较复杂,TS 并不能很好的对数据类型做判断,这时候可以使用 类型断言 自己做类型判断
两种定义方式:
- target
astype (推荐) <type>target (JSX中不可用)
const getLength = (data: string | number): number => {
if((<string>data).length != null) {
return (target as string).length
} else {
return target.toString().length
}
}
显式赋值断言
- 作用: 在 TS 做类型判断时表明变量一定会有值
- 操作符:
!
interface VNode {
tag?: string
style?: {
color?: string,
[styles: string]: any
}
}
const nodeDiv: VNode = {
tag: 'div',
style: {
color: 'red'
}
}
if(nodeDiv.style !== null) {
// 当 tsconfig 开启 compilerOptions.strict 选项时, 没有声明显式赋值断言时会报出警告⚠️
const { color } = nodeDiv.style! // 'red'
}
接口
函数和函数参数
- 类型定义
// 1
function add(x: number, y: number): number {
return x + y
}
// 2
function add({x, y}: {x: number, y: number}): number {
return x + y
}
// 3
let add: (x: number, y: number): number => number
add = (num1, num2) => num1 + num2 // num1、num2默认具有number类型
add = (num1: number, num2: number) => num1 + num2 // 或者直接指定类型
// 4
type Add = (x: number, y: number) => number
let add: Add = (num1, num2) => num1 + num2
// 5
interface Add {
(x: number, y: number): number
}
let add: Add = (num1, num2) => num1 + num2
- 可选参数
// 第三个参数可选
let add = (x: number, y: number, z?:number): number => {
return x + y + (z || 0)
}
add(1, 2)
add(1, 2, 3)
- 函数重载
function multiply(x: number, y: number): number;
function multiply(x: number): number;
function multiply(x: number, y?: any) {
if(y == null) return x * 2
return x * y
}
multiply(1, 2) // 2
multiply(2) // 4
泛型
个人理解: 泛型相当于类型变量
- 类型定义
// T 即为泛型, 泛型的名字可以随便定义
// 1
function copyValue<T>(value: T, length: number): T[] {
return new Array(length).fill(value)
}
copyValue<number[]>([1, 2], 4).map(item => item.length) // [2, 2, 2, 2]
// 或不指定类型
copyValue([1, 2], 4).map(item => item.length) // [2, 2, 2, 2]
// 2
type CopyValue = <K>(value: K, length: number) => K[]
const copyValue: CopyValue = (value, length) => new Array(length).fill(value)
copyValue([1, 2], 4).map(item => item.length) // [2, 2, 2, 2]
// 3
interface CopyValue {
<T>(value: T, length: number): T[]
}
const copyValue: CopyValue = (value, length) => new Array(length).fill(value)
copyValue([1, 2], 4).map(item => item.length) // [2, 2, 2, 2]
// 4. 将泛型定义提取至接口最顶部
interface CopyValue<T> {
(value: T, length: number): T[]
}
const copyValue: CopyValue<number> = (value, length) => new Array(length).fill(value)
copyValue(1, 3).map(item => item + 1) // [2, 2, 2]
- 多泛型
function copyValue<A, B>(value: [A, B], length: number): [A, B][] {
return new Array(length).fill(value)
}
copyValue([1, '2'], 2).map(item => item[0] + item[1]) // ['12', '12']
- 泛型约束
interface LengthAttr {
readonly length: number
}
function getLength<T extends LengthAttr>(val: T): number {
return val.length
}
getLength('123') // 3
getLength([1, 2, 3]) // 3
- 泛型结合索引类型(keyof)
function getVal<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
getVal({name: 'YM', age: 41}, 'name')
类
- 修饰符
public: 默认,可以在类创建的实例中访问,也可以通过this访问private: 只能通过this访问, 且不能在子类中通过this访问protected: 用来修饰constructor时,该类只能用来继承,不能直接实例化;当用来修饰属性或者方法时,只能在该类或者子类的this中访问static: 只能通过类访问readonly: 只读属性
// 父类
class Parent {
// 没有添加修饰符时默认为 public
age: number
// 只能在该类的 this 中访问
private id: string
// 只能在类中访问
static kind: string = 'people'
// 只能在类或子类的 this 中访问
protected score: number
// 只能继承,不能实例化
protected constructor(age: number, id: string, score: number) {
this.age = age
this.id = id
this.score = score
}
getAge() {
return this.age
}
getId() {
return this.id
}
getScore() {
return this.score
}
getKind() {
return Parent.kind
}
}
// 子类
class Child extends Parent {
constructor(age: number, id: string, score: number) {
super(age, id, score)
}
setAge(age: number) {
this.age = age
}
setScore(score: number) {
this.score = score
}
setId(id: string) {
// this.id = id // 报错: 属性“id”为私有属性,只能在类“Parent”中访问
}
}
// static 属性,只能通过类访问
Parent.kind = 'other'
Child.kind || Parent.kind // other
const c = new Child(40, '168888', 1)
// public 属性
c.age || c.getAge() // 40
c.age = 88 // 或 c.setAge(88)
c.age || c.getAge() // 88
// protected 属性
c.getScore() // 1
c.setScore(2)
// c.score // 报错: 属性“score”受保护,只能在类“Parent”及其子类中访问。
c.getScore() // 2
// private 属性
// c.id // 报错: 属性“id”为私有属性,只能在类“Parent”中访问。
c.getId() // 168888
- 抽象类: 抽象类只能通过继承作为父类使用,抽象类中可以定义抽象属性和抽象方法,抽象属性和抽象方法中只包含类型定义,没有具体的实现逻辑
abstract class Parent {
abstract name: string
abstract getName(): string
constructor() {}
}
class Child extends Parent {
name: string
constructor(name: string) {
super()
this.name = name
}
getName() {
return this.name
}
}
const c = new Child('YM')
c.getName() // YM
- 存取器(get/set)
class Parent {
private _data: object
constructor({ data }: { data: () => object }) {
this.data = data()
}
get data() {
return this._data
}
set data(data: object) {
return this._data = data
}
}
const p = new Parent({
data() {
return {
name: 'YM',
age: 41
}
}
})
类实现接口
- 操作符:
implements
interface Common<State> {
state: State
componentDidMount?: () => void
componentWillUnmount?: () => void
}
interface State {
id?: number
currentPage: number
pageSize: number
}
class App implements Common<State> {
state = {
currentPage: 1,
pageSize: 50
}
componentDidMount() {
console.log('component did mount')
}
}
索引类型
- 索引类型查询操作符:
keyof - 索引类型访问操作符:
[]
interface Params {
id: number
name: string
[props: string]: any
}
function getValues<T extends Params, K extends keyof T>(params: T, keys: K[]): T[K][] {
return keys.map(key => params[key])
}
getValues({
id: 111,
name: 'YM',
page: 1
}, ['name', 'page']) // ['YM', 1]
Record类型
Record类型是 TS 内置的映射类型,可以比较方便地实现相同数据类型的定义,拿来定义对象的数据类型
interface Fields {
name: string
id: number
}
type Names = 'YM' | 'LiSa' | 'Siri'
// Record<'YM' | 'LiSa' | 'Siri', { name: string, id: number }>
const obj: Record<Names, Fields> = {
YM: {
name: 'YM',
id: 1
},
LiSa: {
name: 'LiSa',
id: 2
},
Siri: {
name: 'Siri',
id: 3
}
}
// 或者将 Fields 字段值改成统一类型
const obj2: Record<typeof Fields, string> = {
name: 'YM',
id: '1'
}
// 如果不使用 Record 内置类型自己实现
interface Custom {
YM: Fields
LiSa: Fields
Siri: Fields
}
// 可以看的出来,在字段少的时候还好一些,多一些的话有几个字段就需要写几次
映射类型
通过映射类型可以基于其他类型创建新类型
// 1. 给字段添加可选属性
interface VNode {
tag: string
id: string
style: Record<'background' | 'color', string>
}
type PartialType<T> = {
[P in keyof T]?: T[P]
}
type PartialVNode = PartialType<VNode>
const nodeDiv: PartialVNode = {
tag: 'div'
}
// 2. 给字段添加只读属性
type ReadonlyType<T> = {
readonly [P in keyof T]: T[P]
}
type ReadonlyVNode = ReadonlyType<VNode>
const nodeH1: ReadonlyVNode = {
tag: 'h1',
id: 'nodeId',
style: {
color: 'blue',
background: 'red'
}
}
注:TS 已经内置了可选映射类型
Partial和只读映射类型Readonly, 还有两种内置的映射类型是Pick和Record
/**
* Pick 实现方式:
* type Pick<T, K extends keyof T> = {
* [P in K]: T[P]
* }
*/
interface Info {
name: string
age: number
id: string
}
type PickInfo = 'name' | 'age'
const info: Pick<Info, PickInfo> = {
name: 'YM',
age: 88
}
/**
* Record 实现方式:
* type Record<K extends string | number, U> = {
* [P in K]: U
* }
*/
可以通过
+或-符号来增加或移除特定修饰符
// 删除只读属性,添加可选属性
type RemoveReadonlyAddPartial<T> = {
-readonly [P in keyof T]+?: T[P]
}
interface Info {
name: string
age: number
}
const info: RemoveReadonlyAddPartial<Readonly<Info>> = {
age: 78
}
info.age = 79