typescript 高级
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了typescript 高级相关的知识,希望对你有一定的参考价值。
1 类型推断
从右向左流动:
// let a: number 类型从右向左流动
let a = 1;
通过return关键字推断返回值的类型 底部流出
function add(a: number)
return a;
从左向右:
//从左向右
type S = (a: number) => void;
const s: S = (a) =>
// parameter) a: number
console.log(a);
;
mixin:
// mixin
function mixin<T, U>(one: T, two: U)
const result = <T & U> // result需要满足T又要满足U
for (let key in one)
// 不能将类型“T”分配给类型“T & U”。单个无法赋值联合,需要先转换
//result[key] = one[key]
(result as T)[key] = one[key]; //这里赋值T类型需要先断言result为T类型才能赋值。
for (let key in two)
// result[key] = two[key]
(result as U)[key] = two[key];
return result;
return ...one, ...two ; // T & U
const x = mixin( name: "1" , age: 2 );
console.log(x.age, x.name);
2 其他内容
索引访问操作符
interface A
a: string;
b: number;
// const a: string
const a: A["a"] = "1";
映射类型
interface A
a: string;
b: number;
// 映射类型
type ACOPY =
[key in keyof A]: A[key]; // 遍历A,然后值为A[key],相当于复制一个A
;
type ACOPY1 =
[key in keyof A]?: A[key]; // Partial的源码
;
type ACOPY2 = Partial<A>; 将
条件类型
type B<T> = T extends object ? string : number
let b: B<true> // number
let c: B<> // string
分发:
let d: B<|true> // string | number // 满足string | number即可。
找到T中不包含U的部分
type Diff<T, U> = T extends U ? never : T;
// type R = never|never|'c'
type R = Diff<"a" | "b" | "c", "a" | "b">; // a是a|b的子类, b是a|b的子类,他们都会匹配never,而c则不会,
4 内置条件类型
Exclude<T,U>
从T中排除U
type Exclude<T,U> = T extends U ? never : T // Diff
Extract<T,U>
从T中取跟U一样的
type Extract<T,U> = T extends U ? T : never
infer
推断, infer 的作用是让 TypeScript 自己推断,并将推断的结果存储到一个类型变量中,infer 只能用于 extends 语句中。
如ReturnType
type ReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any;
如果T满足与函数,那么就推断函数的返回值赋值给R,然后返回R。
function test(a: string, b: number)
return a: 1 ;
// type Test1 = a: number;
type Test1 = ReturnType<typeof test>;
再如参数: Paramters
type Paramters<T extends (...args: any) => any> = T extends (
...args: infer P
) => any
? P
: any;
如果T满足与函数,就推断参数的类型赋值给P,然后返回P , 返回值是一个元组,否则返回any
type Test2 = Paramters<typeof test>;
// type Test2 = [a: string, b: number]
元组转联合
type ElementOf<T> = T extends Array<infer P> ? P : never; // T是否是数组的子类,是的话推断出类型返回
type Ttuple = [string | number];
// string | number
type Test3 = ElementOf<Ttuple>;
5 内置的工具类型
Partial变为可选 (+?)
type Partial<T> =
[key in keyof T]+?: T[key]; //+? 变成可选项
;
深度变为可选:
interface Test1
a: 1;
b: 2;
type test1 = Partial<Test1>;
// 深度变为可选
interface Test2
a: 1;
b: 2;
c: Test1;
type test22 = Partial<Test2>;
const test2: test22 =
// 类型“”缺少类型“Test1”中的以下属性: a, b
c: ,
;
如上,c一旦存在,他的对象里面的属性是必选的。所以
type DeepPartial<T> =
[key in keyof T]+?: T[key] extends object ? DeepPartial<T[key]> : T[key]; //递归调用
;
// 每次都需要判断是否是一个对象,是的话继续调用DeepPartial
type test33 = DeepPartial<Test2>;
const test3: test33 =
c: ,
;
Require 可选变必选
// 可选变必选,并且只读
type Require<T> =
readonly [key in keyof T]-?: T[key]; // -? 变成必选项
;
interface Test4
name?: 1;
a: 2;
type test4 = Require<Test4>;
PIck只要某个属性
type Pick<T, K extends keyof T> =
// 第二个选项必须是 keyof T的子类
[key in K]: T[key];
;
Omit过滤某个属性
Omit,过滤某个属性,反其道而行,取T中不包含K的就行
type FilterK<T, K> = Exclude<keyof T, K>; //取T中不包含K的属性
type Omit<T, K extends keyof T> = Pick<T, FilterK<T, K>>; // 取T中不包含K的
从T中过滤TU两个对象相同的属性
type Diff<T extends object, U extends object> = Pick<T, Exclude<keyof T, keyof U>>
排除T中跟U一样的属性,获取剩余的属性,再使用Pick获取。
从T中找出TU相同的属性
type InterSection<T extends object, U extends object> = Pick<
T,
Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
>;
取T中有U的,交上U中有T的属性,然后通过pick在T里面获取。
Record
type Record<K extends keyof any, T> =
// keyof的值只有三个 K extends string | number | symbol
[P in K]: T;
;
经常使用的代表对象的就是
type A = Record<"a" | "b", any>; //type A = a: any; b: any;
type B = Record<string, any> ; // type B = [x: string]: any;
6自定义高级类型
Proxy 代理
type Proxy<T> =
get(): T;
set(v: T): void;
;
type Proxify<T> =
[key in keyof T]: Proxy<T[key]>; //通过T【key】获取值,然后继续取Proxy<T>
;
// Proxy 代理
function proxify<T>(obj: T): Proxify<T>
let result = <Proxify<T>>;
for (const key in obj)
type KeyType = typeof key;
result[key] =
get: () => obj[key],
set: (v: T[KeyType]) => (obj[key] = v),
;
return result;
const props =
name: "a",
a: 10,
;
type Props = typeof props;
const newPorps = proxify<Props>(props);
newPorps.a.get(); // a: Proxy<number>
newPorps.name.set("2"); // name: Proxy<string>
反代理:从Proxify变为T
// 反代理
function unProxify<T>(t: Proxify<T>): T
let result = <T>;
for (const key in t)
result[key] = t[key].get();
return result;
// unProps name: string; a: number;
const unProps = unProxify<Props>(newPorps);
unProps.a; // a: number
unProps.name; //name: string
OverWrite重写
有个需求,两个类型A,B,B中跟A相同的需要覆盖A的内容,
如
type Porps = a: number; b: number,d: string ;
type newProps = a: string; b: string; c: number ;
type ReplaceProps = Overwrite<Porps, newProps>
//预期结果是: a:string, b: string, d: string
实现思路:
借助上面实现的找两个对象相同的,和找两个对象不同的。
// 从T找出跟U相同的
type InterSection<T extends object, U extends object> = Pick<
T,
Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
>;
//从T中找出跟U不同的
type Diff<T extends object, U extends object> = Pick<
T,
Exclude<keyof T, keyof U>
>;
type Overwrite<T extends object, U extends object, I = Diff<T,U> & InterSection<U, T> >= Pick<I, keyof I>
这里重点就是第三个泛型I,他无需传值,通过传入的一二泛型获取值。这里的TU顺序很重要。
如第一个Diff<T,U>就是从T里面找出与U不相关的,那就是d: string了。再交上
InterSection<U,T>,从U当中找出与T相关的,就是从newProps找出与Porps相关的,就是a:string, b: string,所以最后得到的结果就是:
type ReplaceProps = Overwrite<Porps, newProps>; // type ReplaceProps = a: string,b: string, d: string
7 模块与命名空间
文件模块:在ts文件的根级别位置含有import 或者 export,那么就会在这个文件创建一个本地的作用域
模块是ts中外部模块的简称,侧重于代码复用
一个模块里的变量函数类等在外部不可见,除非导出。
同个模块相同名字的命名空间会合并。
export namespace zoo
// 同个模块内部同样的名字的命名空间会合并 !!!
export class Dog1
export namespace zoo
export class Dog1 //报错
8类型声明
类型声明可以让我们不需要将js重构为ts,只需要加上声明文件就可以
类型声明在编译的时候会删除
declare关键字表示声明的意思
// 声明命名空间,一个全局变量如果有很多子属性,就可以使用namesqpce。
declare namespace $
function ajax(url: string):void
let name: string
namespace fn
function extend():void
$.fn.extend()
$.ajax('')
自己实现jq的声明文件:
对于jq,他的声明文件导出是export = Jquery, 他是ts的语法,表示可能是es导出,也可能是commonjs导出,具体实现看代码实现
而jq是commonjs导出的,这就导致我们需要import * as Jquery from ‘jquery’,转化以下。或者是tsconfig.json中esModuleInterop设为true,
表示转化es6的语法,才可以import Jquery from ‘jquery’
8 扩展类型
class enum既可以作为类型,也可以作为值
interface type只能作为类型,不能作为值
funciton var let const 只能作为值,不能作为类型
合并声明
同一名称的两个独立声明会被合并成一个单一声明
合并后的声明拥有原先两个声明的特性
可以通过接口合并的特性给一个第三方库扩展类型声明
// 使用命名空间扩展类,需要在类定义后面
namespace D
class Form
username: Form.Item
test: Form.C
d: Form.d //报错 //不可以作为类型
namespace Form
export type Item = string // 定义类型
export class C // 类既可以作为类型,也可以作为值
export let d : string //值不可以做为类型
const test1 = new Form()
const test2 = new Form.C()
Form.Item //不可以作为值
Form.d
// 实用,扩展redux的store
namespace G
import ceateStore, Store from 'redux'
type StoreExt = Store & ext: string // 只能通过type去扩展,不能通过namespace扩展
const store: StoreExt = ceateStore(state=>state)
store.ext
以上是关于typescript 高级的主要内容,如果未能解决你的问题,请参考以下文章