Typescript当中的T,K,V到底是个啥
- 有时候,我们看到下面的代码,当然,这里是简单例子来说
function identity <T> (value:T) : T {
return value;
}
-
其实泛型就是使用字母来代替将要接收的类型,这里的"T"是代表类型的缩写,表示对将要接收类型的一个占位符,占位符可以是任意字母,下面是一些常用的占位符
- T(Type) 表示类型
- K(Key) 表示对象中键的类型
- V(value) 表示对象中值的类型
- E(Element) 表示元素类型
-
如果在函数中使用了泛型,那么我们可以在使用的时候指明类型,也可以不显式指明类型
function identity <T , U>(value: T ,message: U) : T {
console.log(message)
return value;
}
//不指定泛型变量的实际类型
console.log(identity(20,'动感超人'));
//手动指定泛型变量的实际类型
console.log(identity<number,string>(20,'动感超人'))
declare
-
假如我们在html当中引入了jquery插件,那么就会在全局当中增加一个关键字
$
,此时如果我们在ts文件当中的书写关键字$
-
就会发现ts会提示
找不到$,ts2304
,也就是说ts不认识这个全局变量$ -
所以我们可以使用
declare
来定义这个全局变量declare const $ = xxxx
,这样子ts就认识这个全局变量$了 -
注意点
declare
声明不包含具体的实现,也就是说我们只是声明,不做具体处理- declare可以定义全局变量,全局函数,全局枚举,全局类等
- 你看到的xxx.d.ts就是用于放置声明文件的
-
declare和一些声明文件查询:@地址
疑问
- 为什么我们可以在ts中直接写
Math
,JSON
,Object
这些全局对象呢?那是英文typescript已经在文件当中声明了 - 在vie当中,我们可以在
node_modules/vite/client.d.ts
当中就可以看到declare的声明,部分代码如下
declare module '*.module.css' {
const classes: CSSModuleClasses
export default classes
}
// CSS
declare module '*.css' {
const css: string
export default css
}
// images
declare module '*.jpg' {
const src: string
export default src
}
- vite为什么要这样子做呢,因为如果它不这样子做,那么我们在使用下面代码就会报错
//如果vite不declare,那么就会提示找不到模块"./file.css"/或其相应的类型声明
import css from "./file.css";
//如果vite不declare,那么就会提示找不到模块"./abao.jpg"或其相应的类型声明
import logo from "./abao.jpg";
- 从ts2.0开始,declare支持通配符了,就如上声明一样使用了通配符
any类型和unknown类型
-
any
:我不在于它的类型 -
unknown
:我不知道它的类型(可以理解为类型安全的any),使用了unknown,必须要自己进行类型检测后才可以对变量进行操作,否则会报警告或错误 -
对于下列函数,如果是使用
any
类型,是不会有任何报错提示的
function invokeCallBack(callback:any){
try {
callback();
}catch (e){
console.log(e)
}
}
invokeCallBack(1)
- 而对于我们使用了
unknown
,则会提示TS2571: Object is of type 'unknown'.
function invokeCallBack(callback:unknown){
try {
//TS2571: Object is of type 'unknown'.
callback();
}catch (e){
console.log(e)
}
}
invokeCallBack(1)
- 所以对于unknown来说,我们在使用前就必须要判断是否可以执行后才可以没有警告
function invokeCallBack(callback:unknown){
try {
if(typeof callback === 'function'){
callback();
}
}catch (e){
console.log(e)
}
}
invokeCallBack(1)
- 需要注意的是,unknown类型的变量只可以赋值给unknown类型或者是any类型
typescript当中的类型
- never是空集,所以never类型无法被其他类型所赋值
//Type 'string' is not assignable to type 'never'
let num: never = 123;
//Type 'string' is not assignable to type 'never'
let name: never = '超人';
interface
接口定义对象类型,可以使用extends进行扩展
interface Vector1D {x:number}
//等同于
interface Vector3D {
x:number,
y:number,
}
interface Vector2D extends Vector1D {y:number}
//等同于
interface Vector3D {
x:number,
y:number,
z:number
}
interface Vector3D extends Vector2d {z:number};
type和interface的异同
前置知识
type 类型别名
- 针对基本类型和非对象类型的情况非常有用,支持泛型
interface 接口
- interface只能定义对象类型
- 定义接口类型时,可以同时声明对象身上的属性和方法
相同点
- 类型别名和接口都可以用来描述对象或函数
type Point = {
x: number
y:number
}
type SetPoint = (x:number,y:number) => void;
interface Point {
x: number;
y: number; // ";" 或则 "," 或者不写都可以
}
interface SetPoint {
(x: number, y: number): void;
}
- 类型别名和接口都支持扩展
- 类型别名扩展使用
&
交叉运算符进行合并运算,注意,交叉类型中的交叉,并不是指两个类型的交集,而是并集 - 接口扩展使用关键字
interface
- 类型别名扩展使用
//类型别名通过交叉运算符来扩展
type Animal = {
name: string;
};
type Bear = Animal & {
honey: boolean;
};
const bear: Bear = {
name: "熊大",
honey: false,
};
// 接口通过extends来扩展
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
const bear: Bear = {
name: "熊大",
honey: false,
};
- 类型别名和接口都支持相互扩展,但需要注意的是接口只支持使用
extends
关键字来继承,类型也只支持使用&
来完成扩展
type Animal = {
name: string;
};
interface Bear extends Animal {
honey: boolean;
}
const bear: Bear = {
name: "熊大",
honey: false,
};
interface Animal {
name: string;
}
type Bear = Animal & {
honey: boolean;
};
const bear: Bear = {
name: "熊大",
honey: false,
};
不同点
- 类型别名可以为基本类型,联合类型或元组类型定义别名,接口不行
type MyNumber = number; //基本类型定义别名
type StringOrNumber = string | number; //联合类型定义别名
type Point = [number, number]; //元组类型定义别名
- 同名接口会自动合并,而类型别名不会
interface User {
name: string;
}
interface User {
age: number;
}
let user: User = {
name: "李白",
age: 1000,
};
user.name; //李白
user.age; //1000
type User = {
name: string;
};
//标识符“User”重复。ts(2300)
type User = {
age: number;
};
类型别名和接口的一些使用场景
-
使用类型别名的场景
- 定义基本类型的别名时,使用type
- 定义元组类型时,使用type
- 定义函数类型时,使用type
- 定义联合类型时,使用type
- 定义映射类型时,使用type
-
使用接口的场景
- 需要利用接口自动合并特征的时候,使用interface
- 定义对象类型且无需使用type的时候,调用interface
索引签名和Record内置工具类型
-
有时候我们可能会想这样子,我想规定一个对象类型的key只能为字符串,值是任意的,那么要怎么做呢?可以使用索引签名
-
格式语法如下
{[key:KeyType] : ValueType}
- key: 固定的写法
- **keyType: ** key的类型,只支持
string
,number
,symbol
,不能为字面量类型或者是泛型类型,如需要使用字面量或泛型,则需要使用Record内置工具类型 - valueType: value的类型
//比如下面
interface selfName1 {
[key:string] : string
}
const test1:selfName1 = {
name:'李白',
hobby:'吃饭',
}
const selfName2:{[key:string] : string} = {
name:'李白',
hobby:'吃饭',
age:1000,//报错,警告
}
- 需要注意的是
KeyType
只能为string
或者number
或者symbol
不能为其他的值
//错误的keyType
interface selfName3 {
//keytype只能为string,number,symbol
[key:boolean] : string
}
- 使用索引签名也可以和别的已知的key,value使用
interface Options {
[key:string]:string | number | boolean,
timeout:number,//已知的键
}
const option:Options = {
timeout:1000,
errorMessae:'The request timed out!',
isSuccess:false,
}
- 此外,索引签名也可以和模板字符串使用
interface PropChangeHandler {
[key:`${string}Changed`]: () => void;
}
let handlers:PropChangeHandler = {
idChanged: () => {},
nameChanged: () => {},
//报错,因为后面少了一个字符"d",和规定的type不相同
ageChange: () => {},
}
Record内置工具类型和索引签名
- 索引签名类型参数不能为字面量类型或者是泛型类型
type User1 = {
//报错 索引签名类型参数不能为字面量类型或者是泛型类型
[key:"id"]:string,
}
type User2 = {
//报错 索引签名类型参数不能为字面量类型或者是泛型类型
[key:"id" | "name"] :string
}
//有人可能会有疑问,说,哎呀,这个为什么可以
interface PropChangeHandler {
//这个没问题,因为这个不是字面量啊
[key:`${string}Changed`]: () => void;
}
- 而Record却可以包括字面量和泛型
type User3 = Record<"id", string>
const a:User3 = {
id:'2tjawjtiaowt',
}
type User4 = Record<'id' | 'name', string>;
const b:User4 = {
id:'2tjawjtiaowt',
name:'动感超人'
}
原来映射类型是这样子工作的
- ts的一些工具类型,比如说
Pick
,就是从某一个类型当中挑选一部分 - ts中的工具类型操作的是类型,而js当中工具类型操作的是值,
- 下面用ts的pick用js来做下解释,调用ts工具类型(类似函数)使用的是尖括号,js函数则是小括号(一句话,ts用尖括号,js用小括号)
//K extends T 泛型约束的语法,用于约束泛型K类型
type Pick <T,K extends keyof T> = {
[P in K]:T[P]
}
//用js来解释就是
function Pick (obj,keys) {
const result = {};
for(const key of keys){
result[key] = obj[key]''
}
return result;
}
ts内置工具类型中的keyof操作符有啥用
- 首先来说下js当中的
Object.keys
函数作用,Object.keys
会返回对象身上所有可枚举key组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。
const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.keys(object1));
// expected output: Array ["a", "b", "c"]
- 那么ts当中的
keyof
也是,返回对象身上key值组成的联合类型
class Person {
year:number = 2022;
hobby:string = '吃饭';
}
//等同于 type P0Types = 'year' | 'hobby'
type P0Types = keyof Person;
const P01:P0Types = 'year';
const P02:P0Types = 'hobby';
interface Person1Inter {
id:number,
name:string,
}
//等同于 type P1Types = 'id' | 'name'
type P1Types = keyof Person1Inter;
const P11:P1Types = 'id';
const P12:P1Types = 'name';
enum HttpMethods {
Get,
Post,
}
type Methods = keyof typeof HttpMethods;
const P21:Methods = 'Get';
const P22:Methods = 'Post';
- 对原始的数据类型也会获取到对应的联合类型
type K1 = keyof boolean ;// ValueOf
// "toString" | "toFixed" | "toExponential"
// | "toPrecision" | "valueOf" | "toLocaleString"
type K2 = keyof number;
type K3 = keyof any; string | number | symbol
ts为什么keyof typeof可以拿到枚举的联合类型 ?
- 转载自@思否-边城
export enum ab {
'a',
'b'
}
//uni等同于 'a' | 'b'
type uni = keyof typeof ab
TypeScript 中分“类型”和“值”,类型是 TypeScript 认的,一般编译后会消失(不存在于 JS 中)。枚举是比较特殊的定义,虽然定义成类型,但实际是值,它在编译成 JS 之后是一个对象。
TypeScript 中的枚举还分情况,有数值型枚举,也有字符串型枚举,还有混合型的……不讨论复杂了,这里就说数值型的。
enum Hello {
A,
B
}
type X = keyof Hello;
你猜 X
是什么呢?你会发现它包含 toFixed
和 toPrecision
等,是不是感觉像是个 Number 类型的 Key 呢?
再来看看 Number 类型的 …… 果然一样
如果不加
Exclude
运算,会看到keyof Number
看不到键列表
想想,实际上也是,如果这样使用
const a: Hello = Hello.A;
a
的值实际上是一个 Number(仅数值型枚举的情况)
所以 TypeScript 中需要使用 typeof Hello
来取实际的枚举类型(不然就是 Number 的子类型),实际上它是一个接口。
这个类型取出来之后,枚举值名称是被当作类型的 Key 的,所以可以用 keyof
把键值取出来。
ts的映射和泛型
-
ts的映射个人觉得有点像是js当中的map吧,操作都是传入a,经过处理后返回b
-
语法:
{ [P in K]?:T }
-
一些工具库,比如说
Partial
,Required
,Pick
就是通过映射来实现的 -
比如现在有一个需求,需要把这个类型全部改为可选的,代码如下
type User = {
name: string,
password: string,
address: string,
phone: string,
}
//里面的属性需要全部改为可选的
type User1 = {
name?:string,
password?:string,
address?:string,
phone?:string,
}
//你可以使用工具库当中的Partial,也可以自己写一个~
type selfPartial<T> = {
[K in keyof T]?:T[k]
}
- 在映射的过程当中,可以通过添加
+
,-
来添加.移除修饰符(+(加号为默认))- 没有添加前缀,默认使用加号
- 映射类型示例
type Item =
{ a: string;
b: number;
c: boolean
};
// { x: number,y: number }
type T1 = { [P in 'x' | 'y']: number };
//{ x: 'x',y: 'y }
type T2 = { [P in 'x' | 'y']: P };
//{a: string,b: number}
type T3 = { [P in 'a' | 'b']: Item[P] }
//{a: string,b: number,c: boolean}
type T4 = { [P in keyof Item]: Item[P] }
- 映射演示
ts条件类型Conditional Types
-
ts的条件类型和js当中的三元运算符差不多
-
还是一句话,ts操作的是类型,js操作的是值
-
语法
T extends U ? X : Y
- T,U,X,Y都是类型占位符
- 解释:当类型T可以赋值给类型U的时候,就返回X,否则就返回Y
-
先来看一个简单的例子
type I2 = IsString<any>
😕/输出类型为boolean,是因为any这二个值都可以满足,所以就为boolean
type IsString<T> = T extends string ? true :false;
type I0 = IsString<number>;// false
type I1 = IsString<'abc'>;// true
type I2 = IsString<any>;// boolean
type I3 = IsString<never>;// never
除了判断单一类型之外,利用条件类型和条件链,我们还可以同时判断多种类型
- 当传入any的时候,会返回联合类型,因为都符合,所以在ts的三元是一直运算下去的
type TypeName<T> =
T extends string ? string:
T extends number ? number:
T extends boolean ? boolean:
T extends undefined ? undefined:
T extends Function ? Function:
Object;
type T0 = TypeName<string> //string
type T1 = TypeName<'a'> //string
type T2 = TypeName<true> //boolean
type T3 = TypeName<() => void> //Function
type T4 = TypeName<string[]> //Object
type T5 = TypeName<any>; //string | number | boolean | Function | Object
//用js来书写上面的如下
function example(…) {
return condition1 ? value1
: condition2 ? value2
: condition3 ? value3
: value4;
}
// 等价于
function example(…) {
if (condition1) { return value1; }
else if (condition2) { return value2; }
else if (condition3) { return value3; }
else { return value4; }
}
如果传入的是联合类型会发生什么结果
type TypeName<T> =
T extends string ? string:
T extends number ? number:
T extends boolean ? boolean:
T extends undefined ? undefined:
T extends Function ? Function:
Object;
type T10 = TypeName<string | (() => void)>;//string | Function
type T11 = TypeName<string | string[] | undefined>//string | object | undefined
- 为什么T10 和 T11类型返回的是联合类型呢,因为TypeName属于分布式条件类型,在条件类型中,如果被检查的是一个"裸"类型参数,就是没有被数组,元组,或者Promise等包装过,则该条件类型被称为分布式条件类型,对于分布式条件类型来说,当传入的被检查类型是联合类型的时候,在运算过程中就会被依次运算
type Naked<T> = T extends boolean ? "Y" : "N";
//分布式条件类型,判断每一个值是否都符合boolean
//是就为每一个值返回对应的结果
type T0 = Naked<number | boolean>;// "Y" | "N"
//判断传入的T当中的每一个值是否都符合boolean,是就只返回一个"Y",否则只返回一个"N"
type WrappedTuple<T> = [T] extends [boolean] ? "Y" : "N";
//判断传入的T当中的每一个值为boolean类型的数组
type WrappedArray<T> = T[] extends boolean[] ? "Y" : "N";
type WrappedPromise<T> = Promise<T> extends Promise<boolean> ? "Y" : "N";
type T1 = WrappedTuple<number | boolean>;// "N"
type T2 = WrappedArray<number | boolean>;// "N"
type T3 = WrappedPromise<number |boolean>;// "N;
type T4 = WrappedTuple<true | false>;// "Y";
type T5 = WrappedArray<true | false>; //"Y"
- 由以上结果可知,如果条件类型中的类型参数 T 被包装过,该条件类型就不属于分布式条件类型,所以在运算过程中就不会被分解成多个分支。
- 说通俗点就是分布式条件运算返回值为联合类型,会对每一个值进行判断
- 非分布式条件运算就是对整体每一个值进行判断,返回值为普通的类型
Exclude内置工具类型执行流程
- ts内置工具类型
Exclude
作用是传入T,U,将这两个相同的值消除,不同的值提取- 返回值为never代表抛弃
type Exclude<T,U> = T extends U ? never : T;
//返回:c
type T4 = Exclude<'a' | 'b' | 'c','a' | 'b'>;
//返回:never
type T5 = Exclude<'a' | 'b' ,'a' | 'b'>;
ts中的infer
- 现在有一个需求,获取数组当中的类型,和函数返回的类型
type T0 = string[];
type T1 = () => string;
//需求,获取数组的类型和返回值的类型要怎么做?
//做法如下,
type UnpackedArray<T> = T extends (infer U)[] ? U : T;
type T0Result = UnpackedArray<T0>; //string
-
infer
是什么呢?T extends (infer U)[] ? U : T
:是条件类型的语法,而extends字句中的infer U 引入了一个新的类型变量U,用于存储被推断的类型,可以理解为后面这个U将用于存储类型
-
infer
注意的点?infer
只能在条件类型extends
字句中使用,同时infer
声明的类型变量只能在条件类型的true分支中可用
type Wrong1<T extends (infer U)[]> = T[0] // Error type Wrong2<T> = (infer U)[] extends T ? U : T // Error type Wrong3<T> = T extends (infer U)[] ? T : U // Error
-
那么
infer
到底要怎么用呢?- 一句话:要判断的是什么样子,
infer
就长什么样子
- 一句话:要判断的是什么样子,
-
我们再来看看怎么判断函数的返回类型
//我们接着来下面这个的返回值类型
type T1 = () => string;
type T0 = string[];
type UnpackedFunction<T> = T extends (...args:any[]) => (infer U) ? U : T;
//返回 string
type T1Types = UnpackedFunction<T1>
//返回 string[]
type Test = UnpackedFunction<T0>
当遇到函数重载的场景,TypeScript 将使用最后一个调用签名进行类型推断
declare function foo(x:string):number;
declare function foo(x:number):string;
declare function foo(x:string | number):string | number;
type UnpackedFn<T> = T extends (...args:any[]) => (infer U) ? U : T ;
//返回:string | number;
//代表返回类型
type U2 = UnpackedFn<typeof foo>;
利用条件链我们可以实现功能更加强大的 Unpacked 工具类型。
- 还是那句话,使用
infer
,要判断的长什么样子,infer
就长什么样子(括号包起来)
type Unpacked<T> =
T extends (...args:any[]) => (infer U) ? U :
T extends (infer U)[] ? U :
T extends Promise<(infer U)> ? U : T;
type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
type T2 = Unpacked<() => string>; // string
type T3 = Unpacked<Promise<string>>; // string
type T4 = Unpacked<Promise<string>[]>; // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string
利用条件类型和 infer,我们还可以推断出对象类型中键的类型
type User = {
id: number;
name: string;
}
type PropertyType<T> = T extends {id: (infer U),name: (infer R) } ? [U,R] : T;
type U3 = PropertyType<User> // [number, string]
- 在 PropertyType 工具类型中,我们通过 infer 声明了两个类型变量 U 和 R,分别表示对象类型中 id 和 name 属性的类型。若类型配,我们就会以元组的形式返回 id 和 name 属性的类型。那么现在问题来了,在 PropertyType 工具类型中,如果只声明一个类型变量 U,那结果又会是怎样呢?下面我们来验证一下:
type PropertyType<T> = T extends { id: infer U, name: infer U } ? U : T
type U4 = PropertyType<User> // string | number
- 由以上代码可知,U4 类型返回的是 string 和 number 类型组合成的联合类型。为什么会返回这样的结果呢?这是因为在协变位置上,若同一个类型变量存在多个候选者,则最终的类型将被推断为联合类型。
- 然而,在逆变位置上,若同一个类型变量存在多个候选者,则最终的类型将被推断为交叉类型。同样,我们来实际验证一下:
export interface tempType {
a:(x:string) => void,
b:(x:number) => void,
}
type Bar<T> = T extends
{
a:(x:infer U) => void,
b:(x:infer U) => void
}
? U : never;
// string 和 number 类型组合成的交叉类型
// 即最终的类型是 never 类型
type U5 = Bar<tempType>; //string & number 为never
ts的常用工具库
@部分转载CSDN织_网
Partial
- 源码
type Partial<T> = {
[P in keyof T]?: T[P]
}
作用: Partial;把T当中的所有属性都变为可选
详细:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项
interface Foo {
name: string
age: number
}
type Bar = Partial<Foo>
// 相当于
type Bar = {
name?: string
age?: number
}
Required
- 源码
type Required<T> = {
[K in keyof T]-?:T[K]
}
作用:Required:将T所有属性变为必填的
详细:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为必选项
interface Foo {
name: string
age?: number
}
type Bar = Required<Foo>
// 相当于
type Bar = {
name: string
age: number
}
Pick
- 源码
type Pick<T,U extends keyof T> = {
[K in U]:T[K]
}
作用:Pick(A,B);从A当中挑选B并返回
详细:生成一个新类型,该类型拥有 T 中的 K 属性集 ;
新类型 相当于 T 与 K 的交集
interface Foo {
name: string;
age?: number;
gender: string;
}
type Bar = Pick<Foo, 'age' | 'gender'>
// 相当于
type Bar = {
age?: number
gender: string
}
const todo: Bar= {
age?: 3,
gender: 男
};
Exclude
- 源码
- 分布式条件类型来说,当传入的被检查类型是联合类型的时候,在运算过程中就会被依次运算
type Exclude<T,U> = T extends U ? never : T;
作用:Exclude<A,B>; 排除A当中的B
详细: 如果 T 是 U 的子类型则返回 never 不是则返回 T(never可以理解为丢弃值不会返回)
type A = number | string | boolean
type B = number | boolean
type Foo = Exclude<A, B>
// 相当于
type Foo = string
Extract
- 源码
type Extract<T,U> = T extends U ? T : never;
作用:Extract<A,B> 从A中提取B
详细: 如果 T 是 U 的子类型则返回 T 不是则返回 never (never可以理解为丢弃值不会返回)
type A = number | string | boolean
type B = number | boolean
type Foo = Extract<A, B>
// 相当于
type Foo = number | boolean
Omit
- 源码
type Omit<T,K extends keyof any> = Pick<T,Exclude<T,K>>
作用:Exclude<A,B>; 排除A当中的B
详细: 如果 T 是 U 的子类型则返回 never 不是则返回 T(never可以理解为丢弃值不会返回)
type Foo = {
name: string
age: number
}
type Bar = Omit<Foo, 'age'>
// 相当于
type Bar = {
name: string
}
NonNullable
- 源码
type NonNullable<T> = T extends null | undefined ? never : T;
作用:NonNullable;从T中排除null 和 undefined
详细: 从泛型 T 中排除掉 null 和 undefined
type t = NonNullable<'name' | undefined | null>;
//相当于
// type t = "name"
Parameters
- 源码
type Parameters<T extends (...args:any) => any>
= T extends (...args:infer P) => any ? P: never;
作用:Parameters< (形参) => 返回值 > 以元组的形式返回形参
详细: 以元组的方式获得函数的入参类型
type t = Parameters<(name: string) => any>; // type t = [string]
type t2 = Parameters<((name: string) => any) | ((age: number) => any)>; // type t2 = [string] | [number]
ReturnType
- 源码
export type ReturnType<T extends (...args:any) => any>
=
T extends (...arg:any) => infer R ? R : any;
作用:ReturnType< (形参) => 返回值 >
详细: 获得函数返回值的类型
type t = ReturnType<(name: string) => string | number>
// type t = string | number
Uppercase
- 源码
type Uppercase<S extends string> = intrinsic
Uppercase将StringType转为大写,TS以内置关键字intrinsic来通过编译期来实现。
type a = Uppercase<'abcDEF'>
//等同于
type a = 'ABCDEF';
Lowercase
- 源码
type Lowercase<S extends string> = intrinsic;
Lowercase将StringType转为小写,TS以内置关键字intrinsic来通过编译期来实现。
type a = Lowercase<'abcDEF'>
//等同于
type a = 'abcdef';
Capitalize
- 源码
type Capitalize<S extends string> = intrinsic;
Capitalize将StringType首字母转为大写。
type CapitalizeExample = Capitalize<"abc">;
//等同于
type CapitalizeExample = "Abc"
Uncapitalize
- 源码
type Uncapitalize<S extends string> = intrinsic;
Uncapitalize将StringType首字母转为小写
type a = Uncapitalize<'AbcDEF'>
//等同于
type a = 'abcDEF';