01 TS 与 JS
02 使用TS
1. 全局安装
- 安装:
yarn global add typescript - 编译:
tsc first.ts
2. 项目创建时选择ts: Vue3为例子,如上图
03 TS基础类型
八个内置类型
1 number: 数值类型
let num: number // 声明类型
let num2 = 10 // 类型推断:根据赋值进行推断变量的类型
2 string: 字符串类型
let str: string
3 boolean: 布尔类型
let bol: boolean
4 undefined: 未定义
let udf: undefined
5 null: 空值
let nul: null
6 object: 对象类型
let obj: object
7 bigint: 大整数类型,没有位数的限制,精确表示任何位数的整数,用n后缀表达
let bigI: bigint = 100n
8 symbol: 唯一标识符
let symb: symbol = Symbol('sym')
9 数组:可以使用Array 或 Type[] 表达
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
10 元组tuple(新增):表示一个已知数量和类型的数组
let tup: [string, number] = ['hello', 1]
11 any任意类型: 尽量不适使用
let anyT: any
anyT = 1
anyT = true
anyT = '123'
12 void: 函数没有返回值,其实返回了undefined
function voidFn(name: string): void {}
voidFn("123") // undefined
13 never
通常用于抛出异常或 根本就不会有返回值的函数,例如死循环的函数
function throwErr(): never {
throw new Error('an error')
}
throwErr() // Uncaught Error
**never 和 void 的区别**
void 可以被赋值为 null 和 undefined 的类型。
never 则是一个不包含值的类型。
拥有 void 返回值类型的函数能正常运行。
拥有 never 返回值类型的函数无法正常返回 。
14 枚举
用键值对形式表达一个事物属性,默认key为0,依次递增
enum Message {
SUCCESS, // 默认从0标记
ERROR,
WARNING,
}
Message.SUCCESS // 0
Message.ERROR // 1
Message.WARNING // 2
Message[0] // 'SUCCESS'
枚举也可自定义初始key,并依次递增
enum Message2 {
SUCCESS = 100, // 自定义初始值
ERROR,
WARNING,
}
Message.ERROR // 101
enum Message3 {
SUCCESS = 100,
ERROR = 30,
WARNING,
}
Message.SUCCESS // 100
Message.ERROR // 30
Message.WARNING // 31
Message[31] // 'WARNING'
枚举也可定义字符串key,称为字符串枚举
enum Status {
SUCCESS = '200',
ERROR = '400',
}
枚举使用变量或计算方法表达
// 使用变量、计算方法
const test = 2
function func(): number {
return 4
}
enum Status2 {
a = 0,
b = test,
c = func(),
}
枚举内部变量使用
enum M {
A = 'AA',
S = 'ss',
F = A,
}
异构枚举:同时存在 数值和字符串
enum R {
A = 0,
B = 'B',
}
04 接口 interface: 定义对象的类型
用法
// 用法
interface Person {
name: string
age: number
}
let Tony: Person = {
name: 'Tom',
age: 10,
}
// Tony属性必须拥有name 和 age,且name为字符串类型,age为number类型
可选 和 只读属性
interface Person1 {
readonly name: string
age?: number
}
let p: Person1 = {
name: 'Mike',
}
p.name = "Tom" // ts会警告 无法分配到 "name" ,因为它是只读属性。
混合类型:既有对象属性,又有函数
interface PersonLib {
(): void // 函数
sex: number // 属性
run(): void // 方法
}
function getPersonLib(sex: number) {
let plib = (() => {}) as PersonLib
plib.sex = sex || 1
plib.run = () => {
console.log("run!")
}
return plib
}
let pl = getPersonLib(0)
pl()
pl.sex // 0
pl.run() // run!
接口继承、实现类
interface Skill {
skill(): void
}
interface Run extends Skill {
run(): void
}
class Man implements Run {
skill() {
console.log('Skill-skill')
}
run() {
console.log('Run-run')
}
}
多属性检测
在实际开发中,interface的属性检查往往会造成一定的麻烦,我们遇到多余属性检测时,一般采用三种方法绕开
// 1、类型断言
interface Person {
name: string
age: number
}
let p1: Person = {
name: 'Mike',
age: 20,
sex: 0, // 多余的sex
} as Person
// 2、索引签名
interface Person2 {
name: string
[key: string]: string | number
}
let p2: Person2 = {
name: 'Mike',
age: 20,
sex: 0,
}
//3、利用类型兼容性
// 相当于pObj类型推断出{name: string, age: number, sex: number}
// 将pObj赋值给p3,根据类型兼容性,两个变量都有name和age类型,
// 便会认为两个变量相同,绕过属性检查
let pObj = {
name: 'Mike',
age: 20,
sex: 0,
}
let p3: Person = pObj
05 type 类型别名
用法
// 格式
type Person = {
name: string
}
// 字面量类型
type Age = string
// 联合类型
type Sex = string | string[] | number | number[]
// 交叉类型: 常用于多个接口类型合并成一个类型,从而实现接口继承的效果
type MergeType = { name: string; age: number } & { sex: number }
type 与 interface
1、语法不同
2、要用于扩展 extends implaments 时用interface;
要用字面类型、联合类型用type
3、interface可以多次定义,会自动合并, type不能多次定义
06 Generics 泛型
定义: 指在定义函数、接口或类时,不预先指定类型,而在使用时再指定具体类型的一种特性
使用场景:
// 在开发中当一个方法需要传入不同类型的入参数时,需要定义多次或使用any,例如:
function getValue(val: string): string { return val}
function getValue(val: number): number { return val}
// 或
function getValue(val: any): any {return any}
这样处理将使得项目代码臃肿或类型定义不规范维护性低下,在这种情况下我们可用泛型替代
// 使用泛型 Function<Type>(value: Type): Type {}
// 所以上面得例子可以改写成:
function getValue<T>(value: T): T {
return value
}
getValue(123) // 123
getValue('aaa') // aaa
// 泛型的类型名是可自定义的,多个的;
// 不过一般我们都用T=Type,K=Key,V=Value
function getValueTuple<CustomType, K>(value1: CustomType, value2: K): [CustomType, K] {
return [value1, value2]
}
getValueTuple('aaa', 123) // ['aaa', 123], 泛型会根据入参进行类型推断
getValueTuple<string, number>('aaa', 123) // ['aaa', 123] // 当遇到自定义类型时也可进行声明
泛型约束
用extends 对泛型进行约束
interface O {
name: string
}
//
function getObjValue<T extends O>(obj: T): T {
return obj
}
getObjValue() // 警告:应有 1 个参数
getObjValue({ id: 1 }) // 警告: “id”不在类型“O”
getObjValue({ id: 1, name: '123' }) // 正常
泛型类型
1 typeof
用来获取一个变量声明或属性类型
type Identity = {
name: string
}
let iden: Identity = {
name: '',
}
console.log(typeof iden) // Identity
const MyData = [
{ name: 'Alice', age: 15 },
{ name: 'Bob', age: 23 },
{ name: 'Eve', age: 38 },
]
type NewPerson = typeof MyData[number]
// 相当于
type NewPerson = {
name: string
age: number
}
2 keyof
用于生成对象类型键的字符串或数字 的联合类型
type Point = { x: number; y: number }
type P = keyof Point // "x" | "y"
// 如果类型 是string或number索引签名,keyof 返回的是具体类型
type Arrayish = { [n: number]: boolean }
type A = keyof Arrayish // number
type Mapish = { [k: string]: boolean }
type M = keyof Mapish // string | number
// 虽然k这里声明了是string,但在对象中number也会被转换为string识别, obj[0] ==> obj['0'],所以这里是string | number
3 in
用于遍历类型
type Keys = 'name' | 'age' | 'sex'
type Person = {
[k in Keys]: string
}
3 in
用于遍历类型
type Keys = 'name' | 'age' | 'sex'
type Person = {
[k in Keys]: string
}
内置工具类型
4 Partial
将传入的属性变为可选
interface Todo {
title: string
description: string
}
const todo: Todo = {} // 警告:const todo: Todo = {}
type Par = Partial<Todo> //
/** Par的属性都变为可选的
type Par = {
title?: string | undefined
description?: string | undefined
}**/
const todoP: Partial<Todo> = {} // todoP可赋缺失Todo属性的值
5 Required
将传入的属性变为必选
interface Todo2 {
title?: string
description?: string
}
const todo2: Todo2 = {}
type Req = Required<Todo2>
const todoP: Required<Todo2> = {} // 警告 “Required<Todo2>”中的以下属性: title, description
6 Readonly
将传入的属性变为只读
interface Todo3 {
title: string
}
const todo3: Todo3 = { title: ''}
todo3.title = 'to run'
type ReadO = Readonly<Todo3>
const todoRO: Readonly<Todo3> = {title: ''}
todoRO.title = 'readonly' // 初始化后不可再被修改了 所以报警告"title" 是只读属性
7 Record<Keys, Type>
构造一个对象类型,其属性键为Keys,其属性值为Type
type Keys7 = 'miffy' | 'boris'
let cats: Record<Keys7, number>
cats = {
miffy: 123,
boris: 456,
}
interface Type7 {
age: number;
breed: string;
}
let cats2: Record<Keys7, Type7>
cats2 = {
miffy: { age: 1, breed: '短尾猫'},
boris: { age: 1, breed: '蓝猫'},
}
8 Pick<Type, Keys>
从Type中挑选一组属性Keys
interface Todo8 {
title: string
description: string
completed: boolean
}
type TodoPreview = Pick<Todo8, 'title' | 'completed'>
const todo8: TodoPreview = {
title: 'Clean room',
completed: false,
// description: "123" // 这个就变多余的了
}
9 Omit<Type, Keys>
从Type中选择所有属性然后删除Keys
interface Todo9 {
title: string
description: string
completed: boolean
createdAt: number
}
type TodoInfo = Omit<Todo, 'completed' | 'createdAt'>
const todoInfo: TodoInfo = {
title: 'title',
description: 'description',
// completed: "completed" // 这个变多余的了
// createdAt: 1657595356980// 这个也变多余的了
}
10 Exclude<UnionType, ExcludedMembers>
从联合类型中 排除某些成员
type T10 = Exclude<string | number | (() => void), Function>
// 等同于:type T10 = string | number
11 Extract<Type, Union>
从联合类型中 提取某些成员
type T11 = Extract<'a' | 'b' | 'c' | 'd', 'a' | 'd' | 'f'>
// 等同于:type T11 = "a" | "d"
12 NonNullable
过滤Type类型中的 null 和 undefined 类型
type T12 = NonNullable<string[] | string | null | undefined>
// 等同于:type T12 = string | string[]
13 InstanceType
由构造函数类型Type的实例类型来构建一个新类型
class C {
x = 0
y = 0
}
type T0 = InstanceType<typeof C> // C
const T13: T0 = {
x: 0,
y: 1
}
07 TS中的类
1 class
class Point {
// 成员属性
x: number // 不使用修饰符 默认是public
y: number
// 静态属性
static cname: string = 'Greeter' // 实例不可用 类本身可访问
// 修饰符
public cx1 = 1 // 公共的
private cx2 = 2 //私有的:只能该类本身访问
protected cx3 = 3 // 受保护的: 只能该类及其子类可访问
public readonly cx4 = 4 // 只读,不可更改属性值
// 构造函数
constructor(x: number, y: number) {
this.x = x
this.y = y
}
/** 构造函数 - 参数属性
在构造函数中定义参数属性相当于在类中创建了该属性
**/
// constructor(x: number, y: number, public z: number) {
// this.x = x
// this.y = y
// this.y = z
// }
// 成员方法
public getPoint() {
return `${this.x}, ${this.y}`
}
// 静态方法: 静态方法里面的this指向的是类本身,而不是类的实例对象,所以静态方法里面只能访问类的静态属性和方法
static getCname() {
return this.cname
}
}
const point = new Point(1, 2)
point.getPoint() // 1, 2
2 继承 extends
class Child extends Point {
constructor(x: number, y: number) {
super(x, y)
}
// 继承属性、方法,方法重载 不展开了
}
const pointChild = new Child(1, 2)
pointChild.getPoint() //1, 2
3 抽象类
抽象方法 、抽象字段
抽象类不能被实例化,只能被继承;
抽象方法也不能在抽象类中做具体实现,只能在子类中实现且必须实现
所以一般用于抽离对象的共性,由子类继承后根据抽象类的规范进行实现逻辑,实现多态
abstract class Person {
name!: string
// constructor(public name: string) {}
abstract speak(): void
}
class Chinese extends Person {
speak() {
console.log('讲中国话')
}
}
class English extends Person {
speak() {
console.log('English')
}
}
let cc = new Chinese()
let ee = new English()
cc.speak()
ee.speak()
4 接口实现 implements
interface Phone {
type: number
open<T>(style: T): T
}
class Iphone implements Phone {
type: number
constructor(s: number) {
this.type = s
}
open<T>(style: T) {
return style
}
}
const iphone = new Iphone(13)
iphone.open('13pro') // 13pro
4 泛型中使用类
const create = <T>(c: new () => T): T => {
return new c()
}
class Infos {}
create<Infos>(Infos)