ts泛型,映射,条件类型和类型提取infer和一些常用工具库的说明

news2024/11/17 11:54:40

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只能定义对象类型
  • 定义接口类型时,可以同时声明对象身上的属性和方法

相同点

  1. 类型别名和接口都可以用来描述对象或函数
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;
}

  1. 类型别名和接口都支持扩展
    • 类型别名扩展使用&交叉运算符进行合并运算,注意,交叉类型中的交叉,并不是指两个类型的交集,而是并集
    • 接口扩展使用关键字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,
};
  1. 类型别名和接口都支持相互扩展,但需要注意的是接口只支持使用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,
};

不同点

  1. 类型别名可以为基本类型,联合类型或元组类型定义别名,接口不行
type MyNumber = number; //基本类型定义别名
type StringOrNumber = string | number; //联合类型定义别名
type Point = [number, number]; //元组类型定义别名
  1. 同名接口会自动合并,而类型别名不会
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 是什么呢?你会发现它包含 toFixedtoPrecision 等,是不是感觉像是个 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'>;

Exclude演示

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';

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22619.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【k8s】4、资源管理命令-陈述式

文章目录一、资源管理介绍1、资源管理概念1、资源管理方式二、 陈述式对象管理1、基本概念2、基础命令使用3、基本信息查看&#xff08;kubectl get&#xff09;4、增删等操作5、登录pod中的容器6、扩容缩容pod控制器的pod7、标签操作&#xff08;labels&#xff09;8、简单案例…

5G无线技术基础自学系列 | 5G信道结构

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 5G的信道包括逻辑信道、传输信道、物理…

jdk线程池ThreadPoolExecutor优雅停止原理解析(自己动手实现线程池)(二)

ThreadPoolExecutor优雅停止源码分析(自己动手实现线程池v2版本) ThreadPoolExecutor为了实现优雅停止功能&#xff0c;为线程池设置了一个状态属性&#xff0c;其共有5种情况。 在第一篇博客中曾介绍过&#xff0c;AtomicInteger类型的变量ctl同时维护了两个业务属性当前活跃…

SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.8 变更缓存供应商 memcached

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇5 整合第三方技术5.8 变更缓存供应商 memcached5.8.1 memcached 缓存…

如何在 Navicat 16 中仅备份数据库结构 | 数据传输

尽管有少数据库管理员&#xff08;DBA&#xff09;不相信执行定期数据库备份是有用的&#xff0c;但对于如何最好地执行此操作有很多意见。无论你采用哪种方法&#xff0c;都有很多充分的理由保留数据库模式的副本。当发生数据丢失时&#xff0c;你可以从模式中还原数据库结构&…

[附源码]SSM计算机毕业设计二手车交易系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java中的final和常量以及枚举

Java中的final和常量以及枚举final的作用常量常量概述和基本作用常量做信息标志和分类枚举枚举的概念定义枚举类的格式枚举的特征final的作用 1.final关键字是最终的意思&#xff0c;可以修饰&#xff08;类、方法、变量&#xff09; 2.修饰类&#xff1a;表明该类是最终类&am…

数据结构之:链表

链表初体验之单链表 线性表 线性表"线性存储结构" —— 一根线能串起来的数组 存储到物理空间之中 数据需要有相同的数据类型 元素之间的关系 需要是“一对一” 两种存储方式“顺序” 和“链式”链表介绍 分为有头节点的链表和没有头节点的链表。 插入的时候&#xf…

化合物在高通量筛选中的作用

在 1985 年之前&#xff0c;先导物的筛选主要是通过人工进行的&#xff0c;每周处理的样本数量不过几百个&#xff0c;组合化学的出现使得科学家们获取化合物的方式发生了显著变化&#xff0c;他们可以在短时间内合成大量化合物。更重要的是&#xff0c;随着分子生物学和功能基…

【构建ML驱动的应用程序】第 1 章 :从产品目标到 ML 框架

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

SpringBoot框架详细教学保姆级说明

目录 文章目录1.1 简介1.2 特性1.3 四大核心2 springboot入门案例2.1 SpringBoot 项目开发步骤2.2 创建一个 Spring MVC 的 Spring BootController2.3 分析2.4 核心配置文件格式2.5 Spring Boot 前端使用 JSP3 SpringBoot框架Web开发3.1 Spring Boot 集成 MyBatis3.2 DAO 的其它…

D. Make It Round(math)

Problem - D - Codeforces 在Berlandia发生了通货膨胀&#xff0c;所以商店需要改变商品的价格。 商品n的当前价格已经给出。允许将该商品的价格提高k倍&#xff0c;1≤k≤m&#xff0c;k为整数。输出商品的最圆的可能的新价格。也就是在最后有最大数量的零的那个。 例如&…

开发工程师的面经

目录1. static关键字2. 多态是什么&#xff1f;3. ArrayList和LinkList的区别区别ArrayList的扩容机制4. Java是编译型还是解释型&#xff1f;5. 什么是编译&#xff1f;什么是解释&#xff1f;6. String str“abc” 和 String str new String(“abc”)的区别&#xff1f;7. i…

C\C++刷题ADY3

题目来源&#xff1a;力扣 1.第一题 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 思路分析:&#xff08;不带哨兵位的头节点&#xff09; 每次去分析一个节点&#xff0c; 如果节点不是存的是6&#xff0c;就拿节点来尾插 如果节点存的不是6&#xff0c;就把节…

计算机毕业设计ssm+vue基本微信小程序的“香草屋”饮料奶茶点单小程序

项目介绍 随着社会的发展,互联网的迅速发展,5G时代的到来,智能手机的普及,人们的生活方式更加智能一体化,衣食住行也越来越简单快捷,人们的生活也更加趋向于智能化,一台智能手机可以解决生活中的各种难题。为了使人们的生活更加方便,于是各种生活小程序普遍产生, 随着微信小程序…

记一次 .NET 某自动化采集软件 崩溃分析

一&#xff1a;背景 1.讲故事 前段时间有位朋友找到我&#xff0c;说他的程序在客户的机器上跑着跑着会出现偶发卡死&#xff0c;然后就崩掉了&#xff0c;但在本地怎么也没复现&#xff0c;dump也抓到了&#xff0c;让我帮忙看下到底怎么回事&#xff0c;其实崩溃类的dump也…

java项目-第155期ssm班级同学录网站-java毕业设计_计算机毕业设计

java项目-第155期ssm班级同学录网站-java毕业设计_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm班级同学录网站》 该项目分为2个角色&#xff0c;管理员、用户。 用户可以浏览前台,包含功能有&#xff1a; 首页、公告信息、校友风采、论坛信息&#xff0c;用…

vue项目中使用 NProgress 进度加载插件

场景&#xff1a;每次进入页面时&#xff0c;页面顶部都有一个加载条 下来说下这个效果怎么实现的 第一步&#xff1a;下载 NProgress 插件 npm install --save nprogress 第二步&#xff1a;导入 nprogress 并配置 切记&#xff1a;NP 都必须是大写 我是在路由页面中导入的 im…

搜索引擎项目开发过程以及重难点整理

目录认识搜索引擎搜索的核心思路倒排索引介绍项目简介模块管理构建索引模块数据库设计正排索引倒排索引程序入口类Indexer类扫描文档子模块FileScanner类构建文档子模块构建标题parseTitle方法构建urlparseUrl方法构建内容parseContent方法构建正排索引子模块正排索引实体类map…

Nginz静态资源缓存

缓存 缓存&#xff08;cache&#xff09;&#xff0c;原始意义是指访问速度比一般随机存取存储器&#xff08;RAM&#xff09;快的一种高速存储器&#xff0c;通常它不像系统主存那样使用DRAM技术&#xff0c;而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发…