- 发布于
Typescript的个人学习理解
- Authors
- Name
- 田中原
Typescript的个人学习理解
目录
包装类型与字面量类型
TypeScript 对五种原始类型分别提供了大写和小写两种类型。
- Boolean 和 boolean
- String 和 string
- Number 和 number
- BigInt 和 bigint
- Symbol 和 symbol
大写类型同时包含包装对象和字面量两种情况
小写类型只包含字面量,不包含包装对象。
const s1: String = 'hello' // 正确
const s2: String = new String('hello') // 正确
const s3: string = 'hello' // 正确
const s4: string = new String('hello') // 报错
建议只使用小写类型,不使用大写类型。因为绝大部分使用原始类型的场合,都是使用字面量,不使用包装对象。而且,TypeScript 把很多内置方法的参数,定义成小写类型,使用大写类型会报错
泛型
泛型的意义
泛型都是关于将两个或多个值与相同类型相关联!
联合类型
/**
* 联合类型 = 类型A | 类型 B
*/
type NumOrStr = number | string
let num: NumOrStr = 1
let str: NumOrStr = 1
函数
重载签名、实现签名
实现签名
必须包含所有的重载签名
实现签名
不能被直接调用。实现签名
在外部不可见
function makeDate(timestamp: number): Date
function makeDate(m: number, d: number, y: number): Date
/**
* 函数实现时的签名需包含(兼容)重载签名
* 实现签名在实际使用时不能被直接调用
*/
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d)
} else {
return new Date(mOrTimestamp)
}
}
const d1 = makeDate(12345678)
const d2 = makeDate(5, 5, 5)
/**
* 实现签名不能被调用
* No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
*/
const d3 = makeDate(1, 3)
构造签名
/**
* 构造签名
* js通过new操作符调用构造函数式通常是创建对象
* 通过new 关键字可以编写构造签名
*/
class Animal {
numLegs: number = 4
}
// AnimalConstructor是一个构造签名
type AnimalConstructor = new () => Animal
// create函数接受一个构造签名,返回一个Animal实例
function create(c: AnimalConstructor): Animal {
return new c()
}
const a = create(Animal)
直接调用构造函数
/**
* 直接调用构造函数
* 某些对象,如 JavaScript 的 Date 对象,可以使用或不使用 new 进行调用。
* 可以同时使用调用签名和构造签名表达
* NOTE: es6/7 Class语法实例化时必须使用new
* 故此场景仅适用函数构造函数
*/
interface CallOrConstruct {
new (s: string): Date
(n?: number): number
}
参数
extends用于约束参数类型
参数与返回共用一个泛型
参数与返回结构相同,但不是同一个对象,使用
/**
* 使用[泛型]约束时,如果返回值也使用同一个泛型
* 意味着参数与返回值必须是同一个对象,而不能仅仅符合结构
*/
function minimumLength<T extends Type>(obj: T, minimum: number): T {
if (obj.length >= minimum) {
return obj
} else {
/**
* Type '{ length: number; }' is not assignable to type 'Type'.
* '{ length: number; }' is assignable to the constraint of type 'Type',
* but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'.
*/
return obj
}
}
指定函数参数的类型
/**
* 指定函数参数的类型
*/
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2)
}
let arr = combine<string | number>([1, 2, 3], ['hello'])
// ✅ 给泛型指定了联合类型
arr = combine([1, 2, 3], ['hello'])
// ❌Type 'string' is not assignable to type 'number'.
可选参数
/**
* 可选参数
*/
declare fn(x?: number): void
Rest Parameters 剩余形参
- 通过
any[]
、Array<T>
、T[]
、元组
接收所有其他参数
function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x)
}
// 'a' gets value [10, 20, 30, 40]
const a = multiply(10, 1, 2, 3, 4)
Rest Arguments 剩余实参
不限参数数量
// push接收不限量的参数
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
arr1.push(...arr2)
限制参数数量/元组
// Inferred type is number[] -- "an array with zero or more numbers",
// not specifically two numbers
const args = [8, 5];
// (method) Math.atan2(y: number, x: number):number
// Math.atan2 的参数数量实际有限、
const angle = Math.atan2(...args);
// 报错信息:
A spread argument must either have a tuple type or be passed to a rest parameter.
解决方案:as const
const上下文(TODO:理解as const)
// Inferred as 2-length tuple
// as const 推断转为长度为2的元组
const args = [8, 5] as const
// OK
const angle = Math.atan2(...args)
形参解构
function sum({ a, b, c }) {
console.log(a + b + c)
}
sum({ a: 10, b: 3, c: 9 })
function sum({ a, b, c }: { a: number; b: number; c: number }) {
console.log(a + b + c)
}
// 形参过多时另外命名类型更好
type ABC = { a: number; b: number; c: number }
function sum({ a, b, c }: ABC) {
console.log(a + b + c)
}
this
ts默认行为可以自动推导出this
指向。
如果有手动指定this
类型的需要,通过以下方式处理
this参数类型
js规范要求不能有参数名为this
,ts利用此规则在函数内指定this
类型
通常出现在回调式api的情况下
TODO: 此处需加深理解。此处给出的例子也不完整不利于理解。补充更易理解的例子
目前理解:通过设置一个this参数类型,指定了函数体内部this的值
const user = {
id: 123,
admin: false,
becomeAdmin: function () {
this.admin = true
},
}
interface DB {
filterUsers(filter: (this: User) => boolean): User[]
}
const db = getDB()
// 不能使用箭头函数,猜测:否则会导致this绑定至当前代码块或全局
const admins = db.filterUsers(function (this: User) {
return this.admin
})
函数相关的其他类型
void
void 表示不返回值的函数的返回值。只要函数没有任何返回语句,或者不从这些返回语句返回任何显式值,它就是推断类型。
js函数不返回值时,实际获取为undefined。ts的函数在不指定返回值类型时默认推导为void。但实际void不能看做等于undefined
void和undefined区别
返回类型为 void 的上下文类型不会强制函数不返回某些内容。
返回类型为 void 的函数,在实现时可以返回任何其他值,但会被忽略。
type voidFunc = () => void
const f1: voidFunc = () => {
return true
}
const f2: voidFunc = () => true
const f3: voidFunc = function () {
return true
}
但是在将函数返回值赋值给其他变量是,变量会被推断为void类型
// v1,v2,v3类型都是void
const v1 = f1()
const v2 = f2()
const v3 = f3()
object
- 可以指代所有非原始类型
- 基本上不会被使用到
- object ≠ Object (全局类型)
- 函数可以视为object类型,
instanceof Object
unknown
- 类似于any,但无法做任何操作
- 比any类型更安全
- 使用场景待补充
function f1(a: any) {
a.b() // OK
}
function f2(a: unknown) {
a.b()
// Object is of type 'unknown'.
// 类型安全:无法在函数体内调用a.b
}
function safeParse(s: string): unknown {
return JSON.parse(s)
}
// Need to be careful with 'obj'!
// 增加类型安全,防止使用obj.xx?
const obj = safeParse(someRandomString)
never
- never 类型表示从未观察到的值。在返回类型中,这意味着函数抛出异常或终止程序的执行。
- 收窄联合类型时,排除掉所有可能类型后,变量的类型会被推断为
never
function fnUnknown(x: string | number) {
if (typeof x === 'string') {
x // 此时类型为 sting
} else if (typeof x === 'number') {
x // 此时类型为 number
} else {
x // 此时类型已无其他可能,has type 'never'!
}
}
useRef
导致类型“never”上不存在属性“xxx”
React的const textInputRef = useRef();
useEffect(() => {
if (textInputRef && textInputRef.current) {
textInputRef.current?.focus(); // property 'focus' does not exist on type 'never'
}
}, []);
return (
<input ref={textInputRef} />
)
useRef
没有指定元素的类型,应该给useRef泛型指定类型才可以:如const textInputRef = useRef<HTMLInputElement>(null);
Function
- 全局类型 Function 描述了 JavaScript 中所有函数值上的
bind、call、apply
和其他属性。 - 类型为Function时,可以被当函数调用,函数调用返回值为any
- 类型不安全,尽量避免使用
function doSomething(f: Function) {
return f(1, 2, 3)
}
类型运算符
keyof
keyof 是一个单目运算符,接受一个对象类型作为参数,返回该对象的所有键名组成的联合类型。
type MyObj = {
foo: number
bar: string
}
type Keys = keyof MyObj // 'foo'|'bar'
由于 JavaScript 对象的键名只有三种类型,所以对于任意对象的键名的联合类型就是string|number|symbol
。
对于没有自定义键名的类型使用 keyof 运算符,返回never
类型,表示不可能有这样类型的键名。
type KeyT = keyof any // string | number | symbol
type KeyT = keyof object // never
extends
extends继承与扩展
/**
* extends关键字表示继承
* SubType extends SuperType {
* ...SubType新成员
* }
*/
interface Vector1D {
x: number
}
// Vector2D 为 { x: number, y: number }
interface Vector2D extends Vector1D {
y: number
}
// vector3D 为 { x: number, y: number, z: number }
interface Vector3D extends Vector2D {
z: number
}
extends用于逻辑判断
/**
* extends结合三元表达式,实现条件判断
* extends关键字用于条件判断
* ts使用的是结构化类型,只要结构满足则将子类视继承父类
*/
interface Vector1D {
x: number
}
interface Vector2D {
y: number
x: number
}
// 通过extends和三元判断T是否为U的子类型
type SubTypeOf<T, U> = T extends U ? true : false
type A = SubTypeOf<Vector2D, Vector1D> // true
type B = SubTypeOf<Vector1D, Vector1D> // true
type C = SubTypeOf<Vector2D, Vector1D> // false
extends用于约束参数类型
/**
* 通过泛型、extends,约束函数参数类型
* longest的参数被约束为具有length属性的对象
*/
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a
} else {
return b
}
}
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3])
// longerString is of type 'alice' | 'bob'
const longerString = longest('alice', 'bob')
// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100)