function identity <T> (value:T) : T {
return value;
}
其实泛型就是使用字母来代替将要接收的类型,这里的"T"是代表类型的缩写,表示对将要接收类型的一个占位符,占位符可以是任意字母,下面是一些常用的占位符
如果在函数中使用了泛型,那么我们可以在使用的时候指明类型,也可以不显式指明类型
function identity <T , U>(value: T ,message: U) : T {
console.log(message)
return value;
}
//不指定泛型变量的实际类型
console.log(identity(20,'动感超人'));
//手动指定泛型变量的实际类型
console.log(identity<number,string>(20,'动感超人'))
假如我们在html当中引入了jquery插件,那么就会在全局当中增加一个关键字$
,此时如果我们在ts文件当中的书写关键字$
就会发现ts会提示找不到$,ts2304
,也就是说ts不认识这个全局变量$
所以我们可以使用declare
来定义这个全局变量declare const $ = xxxx
,这样子ts就认识这个全局变量$了
注意点
declare
声明不包含具体的实现,也就是说我们只是声明,不做具体处理declare和一些声明文件查询:@地址
Math
,JSON
,Object
这些全局对象呢?那是英文typescript已经在文件当中声明了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不declare,那么就会提示找不到模块"./file.css"/或其相应的类型声明
import css from "./file.css";
//如果vite不declare,那么就会提示找不到模块"./abao.jpg"或其相应的类型声明
import logo from "./abao.jpg";
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)
function invokeCallBack(callback:unknown){
try {
if(typeof callback === 'function'){
callback();
}
}catch (e){
console.log(e)
}
}
invokeCallBack(1)
//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 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;
}
&
交叉运算符进行合并运算,注意,交叉类型中的交叉,并不是指两个类型的交集,而是并集extends
//类型别名通过交叉运算符来扩展
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;
};
使用类型别名的场景
使用接口的场景
有时候我们可能会想这样子,我想规定一个对象类型的key只能为字符串,值是任意的,那么要怎么做呢?可以使用索引签名
格式语法如下
{[key:KeyType] : ValueType}
string
,number
,symbol
,不能为字面量类型或者是泛型类型,如需要使用字面量或泛型,则需要使用Record内置工具类型
//比如下面
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
}
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: () => {},
}
type User1 = {
//报错 索引签名类型参数不能为字面量类型或者是泛型类型
[key:"id"]:string,
}
type User2 = {
//报错 索引签名类型参数不能为字面量类型或者是泛型类型
[key:"id" | "name"] :string
}
//有人可能会有疑问,说,哎呀,这个为什么可以
interface PropChangeHandler {
//这个没问题,因为这个不是字面量啊
[key:`${string}Changed`]: () => void;
}
type User3 = Record<"id", string>
const a:User3 = {
id:'2tjawjtiaowt',
}
type User4 = Record<'id' | 'name', string>;
const b:User4 = {
id:'2tjawjtiaowt',
name:'动感超人'
}
Pick
,就是从某一个类型当中挑选一部分//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;
}
Object.keys
函数作用,Object.keys
会返回对象身上所有可枚举key组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。const object1 = {
a: 'somestring',
b: 42,
c: false
};
console.log(Object.keys(object1));
// expected output: Array ["a", "b", "c"]
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
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的映射个人觉得有点像是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的条件类型和js当中的三元运算符差不多
还是一句话,ts操作的是类型,js操作的是值
语法
T extends U ? X : Y
先来看一个简单的例子
type I2 = IsString
😕/输出类型为boolean,是因为any这二个值都可以满足,所以就为booleantype 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
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
// 裸类型
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"
Exclude
作用是传入T,U,将这两个相同的值消除,不同的值提取(利用了条件类型)
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'>;
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>
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>;
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
type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string
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]
type PropertyType<T> = T extends { id: infer U, name: infer U } ? U : T
type U4 = PropertyType<User> // 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
@部分转载CSDN织_网
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
}
type Required = {
[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
}
type Pick = {
[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: 男
};
type Exclude<T,U> = T extends U ? never : T;
作用:Exclude; 排除A当中的B
详细: 如果 T 是 U 的子类型则返回 never 不是则返回 T(never可以理解为丢弃值不会返回)
type A = number | string | boolean
type B = number | boolean
type Foo = Exclude<A, B>
// 相当于
type Foo = string
type Extract<T,U> = T extends U ? T : never;
作用:Extract 从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
type Omit<T,K extends keyof any> = Pick<T,Exclude<T,K>>
作用:Exclude; 排除A当中的B
详细: 如果 T 是 U 的子类型则返回 never 不是则返回 T(never可以理解为丢弃值不会返回)
type Foo = {
name: string
age: number
}
type Bar = Omit<Foo, 'age'>
// 相当于
type Bar = {
name: string
}
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"
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]
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
type Uppercase<S extends string> = intrinsic
Uppercase将StringType转为大写,TS以内置关键字intrinsic来通过编译期来实现。
type a = Uppercase<'abcDEF'>
//等同于
type a = 'ABCDEF';
type Lowercase<S extends string> = intrinsic;
Lowercase将StringType转为小写,TS以内置关键字intrinsic来通过编译期来实现。
type a = Lowercase<'abcDEF'>
//等同于
type a = 'abcdef';
type Capitalize<S extends string> = intrinsic;
Capitalize将StringType首字母转为大写。
type CapitalizeExample = Capitalize<"abc">;
//等同于
type CapitalizeExample = "Abc"
type Uncapitalize<S extends string> = intrinsic;
Uncapitalize将StringType首字母转为小写
type a = Uncapitalize<'AbcDEF'>
//等同于
type a = 'abcDEF';