前言
阅读笔记, 比较凌乱, 后面统一梳理,完善
目录
进阶
知识点
- 在if条件使用枚举报错:“恒为xx, 因为类型xx和xx没有重叠”的原因分析
- 包含extends的泛型传参和普通类型赋值的区别
type A1 = number | string extends string ? string : number; // number type P<T> = T extends string ? string : number; type A2 = P<string | number>; // string | number; - keyof any意味着什么呢?
扩展阅读
- 理解TypeScript 中 any 和 unknown 使用unknown来代替any,使用类型断言来来缩小类型范围
- Typescript中的extends关键字
项目配置
项目配置主要要使用项目配置里的typescript版本,而非电脑全局版本. 避免版本不一样, 造成开发,编译产生的结果不一样. 配置方法:
1. 切换到`.ts`文件
2. 在vs code 地址栏下方, 点击 `Typescript:Version`扩展项, 然后点击`Use Workspace Version`选项. 或者在项目根目录.vscode文件中的setting.json文件中增加配置项: `typescript.tsdk: "node_modules/typescript/lib"`
类型
集合类型
string, number, boolean, symbol, null, undefined, bigint
字面量类型
使用一个字面量作为类型. 比如this is string
TypeScript 支持是那种字面量类型: 字符串字面量类型, 数字字面量类型, 布尔字面量类型. 对应的字符串字面量, 数字字面量, 布尔字面量分别拥有与其值一辆的字面量类型.
let specifiedStr: 'this is string' = 'this is string'
let specifiedNum: 1 = 1
let specifiedBoolan: false = false
let str: string = 'this is string'
specifiedStr = str; //ts(2322), 类型string不能赋值给类型this is string
str = specifiedStr; // ok
字面量类型是集合类型的子类型, 它是集合类型的一种更具体的表达.
使用let和const定义的变量值相同, 但是类型不一致
// 不可变更的常量
const strConst = 'this is string'
// 可变变量
let str = 'this is string'
使用let声明了一个可变变量, 使用const声明了一个不可变变量.
在缺省类型注解的情况下, 不可变变量的类型为赋值字面量的类型, 可变变量的类型会转换为赋值字面量类型的父类型(比如const str的类型为'this is string', let num = 1, num的类型为number).
. 未缺省情况下, const声明变量的类型即声明的类型.
类类型
声明类的时候, 其实我们也同时声明了一个特殊的类型(确切的将是一个接口类型), 这个类型的名字就是类名, 表示类实例的类型; 在定义类的时候, 我们声明的除构造函数外所有属性, 方法的类型就是这个特殊类型的成员.
- 派生类如果包含一个构造函数, 则必须在构造函数中调用super方法, 这是Typescript强制执行的一条重要规则.
- 访问修饰符: public, private, protected
- 只读修饰符: readonly
- 存取器: getter, setter
class Dog {
private lastName: string = 'stack'
get myLastName() { return this.lastNmae }
set myLastName(name: string) {
if (name === 'xxx') {this.lastName = name}
else { console.error('xmxmxm') }
}
}
- 静态属性: 实例属性和方法,只有类在实例化时才会被初始化. 静态属性可以通过类名直接调用
class MyArray {
static dispalyName = 'myArray'
static isArray (obj: unknown) {
return Object.prototype.toString.call(obj).slice(8, -1) === 'Array'
}
}
console.log(MyArray.displayName) // myArray
console.log(MyArray.isArray([])) // true
基于静态属性的特性, 我们往往会把与类相关的常量, 不依赖实例this上下文的属性和方法定义为静态属性, 从而避免数据冗余, 进而提升运行性能.
抽象类
一种不能被实例化, 仅能被子类继承的特殊类.
我们可以使用抽象类定义派生类需要实现的属性和方法, 同时也可以定义其他被继承的默认属性和方法.
抽象类与接口区别
接口只能定义成员和类型, 抽象类可以定义成员,类型和方法实现
abstract class Adder {
abstract x: number;
abstract y: number; // 需要继承实现的
abstract add(): number;
displayName = 'Adder'; // 不需要继承实现
addTwice(): number {
return (this.x + this.y) * 2
}
}
class NumberAdder extends Adder {
x: number; // 基类使用abstract修饰, 子类必须重新声明
y: number;
add(): number {
return this.x + this.y
}
}
使用
- Number、String、Boolean、Symbol包装类型与基础类型不同,注意区分
- ts(2322)是一个静态类型检查的错误,在注解的类型和赋值的类型不同的时候就会抛出这个错误
- 数组类型的值只有显示添加了元组类型注解后(或者使用 as const,声明为只读元组),TypeScript 才会把它当作元组,否则推荐出来的类型就是普通的数组类型
- any 除非有重组的理由, 否则应尽量避免使用any, 并且开启禁用隐式any的设置
- unkown(3.0+) 在类型上更安全. 比如可以加将任意类型的值复制给unknown, 但anknown类型的值只能复制给unknown或any. 使用unknown后, typescript会对它做类型检测. 但是, 如果不缩小类型(type narrowing), 我们对unknown的任何操作都会出现错误
- void, undefined, null 用到的不多, 可以将undefined的值或类型时undefined的变量赋值给void类型, 反过是void但值是undefined的变量不能赋值给undefined类型.
let undeclared:undefined = undefined
let unusable:void = undefined
unusable = undeclared // ok
undeclared = unusable // ts:2322
概念
类型推断, 上下文推断
ts可以根据赋值类型或者上下文,推断出变量的类型
赋值推断
let x1 = 42; //number
let x2: number = x1; //ok
上下文推断
type Adder = (a:number, b:number) => number;
const add:Adder = (a, b) => a + b // 推断出a,b为number类型
字面量类型拓宽 Literal widening
可变变量的类型转换为父类型的设计我们称之为"literal widening", 也就是字面量类型拓宽.
const str = 'this is string' // 类型为'this is string'
const str1 = 'this is string' // 类型为string
specifiedStr = str; // ts(2322)
str = specifiedStr
应用
// 字符串字面量类型
type Direction = 'up' | 'down'
// 布尔字面量类型
type isEnable = true | false
// 数字字面量类型
type margin: 0 | 2 | 4
类型拓宽(Type Widening)
非严格模式下, TypeScript 对某些特定类型有类似字面量类型拓宽的设计. 比如对null和undefined的类型拓宽. 通过let, var声明的变量如果满足为显示声明类型注解且被赋予了null或者undefined值, 则推断出这些变量的类型为any.
let x = null; // 类型扩宽为any
let y = undefined; // 类型拓宽为any
type X = typeof x; // X 为any类型
类型缩小 Type Narrowing
通过某些操作, 将变量的类型有一个较为宽泛的集合缩小到较小, 较明确的集合.
let func = (param: any) => {
if (typeof param === 'string') {
// 明确param为string类型, 因此可以调用String原型链上的方法
return param.toUpperCase()
} else if (typeof param === 'number') {
return param.toFixed(2)
}
return null
}
联合类型
使用|表示联合类型(string | undefined, 表示为string或undefined类型)
交叉类型
freshness
鼠标分别放在x1, x2, x3, x4 上方, x1, number, x2 string, x3 number, x4 number
interface P1 {
name: string
}
interface P2 extends P1 {
age: number
}
function convert(x: P1): number
function convert(x: P2): string;
function convert(x: P1 | P2): any {}
const x1 = convert({name: ''}) // number
const x2 = convert({name: '', age: 0}) // string
const x3 = convert({name: ''} as P1) // number
const x4 = convert({name: '', age: 0} as P2) // number
关于x2和x4使用断言和不使用断言的解释:
- 函数重载是从上往下匹配, 针对x4, 因为P2继承P1, 所以类型为P2的参数和类型为P1的参数一样匹配到第一个重载, 进而推断出来的是number. 为了避免这种情况, 可以将covert的两个重载函数声明调换位置.
- TS 里对于对象字面量有一个 freshness 的概念,使用断言是为了避开它,如果不使用断言,匹配会对比对象的每一个属性,因此 x2 就匹配不到 P1,而会匹配上 P2。