系列文章目录
1.必知必会的特性
2.枚举
3.泛型
一、必知必会的特性
接口的作用:就是为这些类型命名和为你的代码或第三方代码定义契约
Readonly(接口) 可以声明更加严谨的可读属性,亦或者变量
readonly
vs const
最简单判断该用readonly
还是const
的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const
,若做为属性则使用readonly
。
条件类型(Conditional Type)
export type DeepReadonly<T> = T extends Builtin
? T
: T extends Map<infer K, infer V>
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
: T extends ReadonlyMap<infer K, infer V>
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
: T extends WeakMap<infer K, infer V>
......
其中DeepReadonly
通过extends
的方式继承父类然后通过? :
表达式来进行一个类型三目运算符
的操作进行一个类型的条件判断。
通过一个简单的案例来进行理解,当泛型T
为string
类型的时候,那么B
为1
,反之为2
。可以看到同样的一个类型,因为传入的泛型T
不一样,结果自然而然的有了出入。
namespace
命名空间(namespace)
是一个比较常见的东西,它常用于组织一份类型区域防止类型之间的重命名冲突,需要配置 declare
输出到外部环境才能够使用,非常便捷的在于使用declare namespace
在工程项目中可以不需要引入任何类型而直接可以访问。
declare namespace JSONValue{
type:a =number;
}
const age: JSONvalue.a ='2'
declare
declare
是用于声明形式存在的。
declare var/let/const
用来声明全局的变量。declare function
用来声明全局方法(函数)declare class
用来声明全局类declare namespace
用来声明命名空间declare module
用来声明模块
模块类型
首先,知道index.js
导出是一个对象,那么declare const
一个类型出来,然后通过export = config
的形式对导出进行声明。那么在通过import { xxx } from '@/config
就可以获悉具体的类型了。
declare const config : BaseConfig& EvnConfig
export config
(propety) baseApi: `http://${string}| https://${string}` from baseApi
import {baseApi} from '@/config'
模板字符串类型
type EventName<T extends string> = `${T}Changed`;
type T0 = EventName<'foo'>; // 'fooChanged'
type T1 = EventName<'foo' | 'bar' | 'baz'>; // 'fooChanged' | 'barChanged' | 'bazChanged'
type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`;
type T2 = Concat<'Hello', 'World'>; // 'HelloWorld'
字符串模板中的联合类型会被展开后排列组合:
type T3 = `${'top' | 'bottom'}-${'left' | 'right'}`;
// 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
TS 中新增了 uppercase
, lowercase
, capitalize
, uncapitalize
这些关键字,用于对模板粒度字符串变量进行处理。
type Cases<T extends string> = `${uppercase T} ${lowercase T} ${capitalize T} ${uncapitalize T}`;
type T11 = Cases<'bar'>; // 'BAR bar Bar bar'
配合 infer
特别强大的一点是,模板字符串可以通过 infer
关键字,实现类似于正则匹配提取的功能:
type MatchPair<S extends string> = S extends `[${infer A},${infer B}]` ? [A, B] : unknown;
type T20 = MatchPair<'[1,2]'>; // ['1', '2']
type T21 = MatchPair<'[foo,bar]'>; // ['foo', 'bar']
通过 ,
分割左右两边, 再在左右两边分别用一个 infer
泛型接受推断值 [${infer A},${infer B}]
,就可以轻松的重新组合 ,
两边的字符串。
配合 ...
拓展运算符和 infer
递归,甚至可以实现 Join
功能:
type Join<T extends (string | number | boolean | bigint)[], D extends string> =
T extends [] ? '' :
T extends [unknown] ? `${T[0]}` :
T extends [unknown, ...infer U] ? `${T[0]}${D}${Join<U, D>}` :
string;
type T30 = Join<[1, 2, 3, 4], '.'>; // '1.2.3.4'
type T31 = Join<['foo', 'bar', 'baz'], '-'>; // 'foo-bar-baz'
实现 Vuex namespace 推断:
type VuexOptions<M, N> = {
namespace?: N,
mutations: M,
}
type Action<M, N> = N extends string ? `${N}/${keyof M & string}` : keyof M
type Store<M, N> = {
dispatch(action: Action<M, N>): void
}
declare function Vuex<M, N>(options: VuexOptions<M, N>): Store<M, N>
const store = Vuex({
namespace: "cart" as const,
mutations: {
add() { },
remove() { }
}
})
store.dispatch("cart/add")
store.dispatch("cart/remove")
实现 lodash get 函数:
type PropType<T, Path extends string> =
string extends Path ? unknown :
Path extends keyof T ? T[Path] :
Path extends `${infer K}.${infer R}` ? K extends keyof T ? PropType<T[K], R> : unknown :
unknown;
declare function get<T, P extends string>(obj: T, path: P): PropType<T, P>;
const obj = { a: { b: {c: 42, d: 'hello' }}};
const value = get(obj, "a.b.c")
函数重载
函数重载
是一个非常常用的特性,它大多数用于多态函数。大多数同学可能都不怎么使用。但是它能够定义不同的参数类型。需要有多个重载签名
和一个实现签名
。
重载签名
:就是对参数形式的不同书写,可以定义多种模式。实现签名
:对函数内部方法的具体实现。
getter/setter
get/set存取器
是在class
当中比较实用的一个功能,它保证了类中变量的私有化。在外部时时不能直接对其更改的,如果大家了解javaBean
的话理解起来并不是很困难。
在class
中声明一个带_
下标的变量,那么就可以通过get
, set
对其进行设置值。
在实例中当我们调用.name
的时候,其实本身就是调用了其get
的方式,而设置值时,则是调用set
方法,
需要注意的是,
._name
值也输出了,但是TypeScript
会进行提示你._name是私有的
不允许你访问。
二、枚举
枚举可以看一篇阿宝哥的小文章一文让你彻底掌握 TS 枚举
https://juejin.cn/post/6844904112669065224#heading-3
三、泛型
简单的函数泛型
function setSex<T> (sex: T) { } setSex<'男'>('女')
泛型类
class Person<T> {
private sex: T;
constructor(readonly type: T) {
this.sex = type;
}
}
const person = new Person<'男'>('女')