typeScritp的高级函数

1.交叉类型

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种情况的场合很多!) 下面是如何创建混入的一个简单例子:

const mergeFunct = <T,U>(obj1:T,obj2:U):T&U =>{
    // let res = {} as T & U
    let res = <T&U>{}
    res = Object.assign(obj1,obj2)
    return res
}
mergeFunct({a:'a'},{b:'b'})

2.联合类型

联合类型与交叉类型很有关联,但是使用上却完全不同。 偶尔你会遇到这种情况,一个代码库希望传入 number或 string类型的参数。 例如下面的函数:

type1 | type2 | type3 |type4

const getLengthFun = (context:number | string):number=>{
    if(typeof context === 'string'){
        return context.length
    }else{
        return context.toString().length
    }
}

3.类型的保护

3.1 typeof 的类型保护
这些typeof类型保护只有两种形式能被识别: typeof v === "typename"和 typeof v !== “typename”, "typename"必须是 “number”, “string”, "boolean"或 “symbol”。 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。

const ValueList = ['abc',123]
const getRandomValue = ()=>{
    const num = Math.random() * 10
    if(num > 5){
        return ValueList[0]
    }else{
        return ValueList[1]
    }
}



const item = getRandomValue()
function isString(type:number | string):type is string{  //类型保护基本上就是这样子的函数来约束输入的值
    return typeof type === 'string'
}

if(isString(item)){
    console.log(item.length)
}else{
    console.log(item.toFixed())
}

3.2 instanceof的类型保护

instanceof类型保护是通过构造函数来细化类型的一种方式。 比如

	class CressByClass1{
    age = 213
}

class CressByClass2{
    name = 'asd'
}

function getRandom(){
    return Math.random() * 10 < 5 ? new CressByClass1() : new CressByClass2()
}

const item1 = getRandom()

if(item1 instanceof CressByClass1){
    console.log(item1.age)
}else{
    console.log(item1.name)
}

4.null / undefined

TypeScript具有两种特殊的类型, null和 undefined,它们分别具有值null和undefined.
默认情况下,类型检查器认为 null与 undefined可以赋值给任何类型。 null与 undefined是所有其它类型的一个有效值。 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。

let s = "foo";
s = null; // 错误, 'null'不能赋值给'string'
let sn: string | null = "bar";
sn = null; // 可以

sn = undefined; // error, 'undefined'不能赋值给'string | null'

注意,按照JavaScript的语义,TypeScript会把 null和 undefined区别对待。 string | null, string | undefined和 string | undefined | null是不同的类型。

5.类型断言

	function getLengthStr(num:number | null):string{
    function gerRes(prefix:string){
        return prefix + num!.toFixed().toString()
    }

    num = num || 0.1

    return gerRes('lison-')
}

console.log(getLengthStr(9.39999))

6.类型别名

类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。

type TypeString = string;
let str2 : TypeString

type PositionType<T> = {x:T,y:T}

const position1:PositionType<number> = {
    x : 1,
    y : -1
}

const position2:PositionType<string> = {
    x:'x',
    y:'y'
}

type Childs<T> = {
    current:T,
    child?:Childs<T>
}

使用接口也可以达到类型别名的作用

               使用接口也可以起到类型别名的作用
interface Alias {
    num:number
}

interface Interface{
    num:number
}

let _alia:Alias = {
    num:1
}

let inter:Interface = {
    num:2
}

_alia = inter


7.自变量类型

字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串。

type Name = 'Lison'
const name3:Name = 'Lison'

type Direction = 'north' | 'east' | 'south' | 'west'

function getDirectionFirstLetter(direction:Direction){
    return direction.substr(0,1)
}
console.log(getDirectionFirstLetter('east'))


type Age = 18;
interface InfoInterface {
    name:string;
    age:Age
}

const info:InfoInterface = {
    name:'aa',
    age:18
}

8.可辨识联合

你可以合并单例类型,联合类型,类型保护和类型别名来创建一个叫做 可辨识联合的高级模式,它也称做 标签联合或 代数数据类型。 可辨识联合在函数式编程很有用处。 一些语言会自动地为你辨识联合;而TypeScript则基于已有的JavaScript模式。 它具有3个要素:

  1. 具有普通的单例类型属性— 可辨识的特征。
  2. 一个类型别名包含了那些类型的联合— 联合。
  3. 此属性上的类型保护。
interface Square {
    kind:'square',
    size:number
}

interface Rectangle {
    kind:'rectangle',
    width:number,
    height:number
}

interface Circle {
    kind:'circle',
    radius:number
}

type Shape = Square | Rectangle | Circle

function asserNever(value:never):never {
    throw new Error('Unexpected onject' + value)
}

function getArea(s:Shape):number{
    switch(s.kind){
        case 'square':
            return s.size * s.size;
            break
        case 'rectangle':
            return s.height * s.width
            break
        case 'circle':
            return Math.PI * s.radius ** 2
            break;
        default: 
            return asserNever(s)
    }
}

9.this 类型

this类型表示的是某个包含类或接口的 子类型。 这被称做 F-bounded多态性。 它能很容易的表现连贯接口间的继承。

return this 之后就可以直接链式编程
class Counter {
    constructor(public count:number = 0){}
    
    add(value:number){
        this.count += value
        return this
    }

    subtract(value:number){
        this.count -= value
        return this
    }
}

const counter = new Counter(10)

console.log(counter.add(90).subtract(77))

class PowCounter extends Counter {
    constructor(public count:number = 0){
        super(count)
    }

    pow(value:number){
        this.count = this.count ** value
        return this
    }
}

const powcount = new PowCounter(2)

console.log(powcount.pow(3).add(1).subtract(7))

10.索引类型 keyof

使用索引类型,编译器就能够检查使用了动态属性名的代码。 例如,一个常见的JavaScript模式是从对象中选取属性的子集。

function pluck(o, names) {
    return names.map(n => o[n]);
}

下面是如何在TypeScript里使用此函数,通过 索引类型查询和 索引访问操作符:

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map(n => o[n]);
}

interface Person {
    name: string;
    age: number;
}
let person: Person = {
    name: 'Jarid',
    age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]

编译器会检查 name是否真的是 Person的一个属性。 本例还引入了几个新的类型操作符。 首先是 keyof T, 索引类型查询操作符。 对于任何类型 T, keyof T的结果为 T上已知的公共属性名的联合。 例如:

let personProps: keyof Person; // 'name' | 'age'

keyof Person是完全可以与 ‘name’ | 'age’互相替换的。 不同的是如果你添加了其它的属性到 Person,例如 address: string,那么 keyof Person会自动变为 ‘name’ | ‘age’ | ‘address’。 你可以在像 pluck函数这类上下文里使用 keyof,因为在使用之前你并不清楚可能出现的属性名。 但编译器会检查你是否传入了正确的属性名给 pluck:

pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age'

第二个操作符是 T[K], 索引访问操作符。 在这里,类型语法反映了表达式语法。 这意味着 person[‘name’]具有类型 Person[‘name’] — 在我们的例子里则为 string类型。 然而,就像索引类型查询一样,你可以在普通的上下文里使用 T[K],这正是它的强大所在。 你只要确保类型变量 K extends keyof T就可以了。 例如下面 getProperty函数的例子:

function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
    return o[name]; // o[name] is of type T[K]
}

getProperty里的 o: T和 name: K,意味着 o[name]: T[K]。 当你返回 T[K]的结果,编译器会实例化键的真实类型,因此 getProperty的返回值类型会随着你需要的属性改变。

let name: string = getProperty(person, 'name');
let age: number = getProperty(person, 'age');
let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'

索引类型和字符串索引签名

keyof和 T[K]与字符串索引签名进行交互。 如果你有一个带有字符串索引签名的类型,那么 keyof T会是 string。 并且 T[string]为索引签名的类型:

interface Map<T> {
    [key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number

11.Readonly Partial Pick Record

Readonly 设置里面的全部为只读 – 系统自带的可以直接使用

interface Info1 {
    age:number,
    name:string,
    sex:string
}

type ReadyonlyType<T> = {
    readonly [P in keyof T] : T[P]   //这里面的 [P in keyof T] 相当于js 里面的数组的 for 循环 P 就相当于里卖的item.
}

type ReadonlyInfo1 = Readonly<Info1>

let info1:ReadonlyInfo1 = {
    age:19,
    name:'lison',
    sex:'man'
}

Partial 设置全部为可选 – 系统自带的可以直接使用

interface Info1 {
    age:number,
    name:string,
    sex:string
}

type PartialType<T> = {
    Partial  [P in keyof T] : T[P]  
}

type PartialInfo1 = Partial<Info1>

let info1:PartialInfo1 = {
    age:19,
    name:'lison',
    sex:'man'
}

Pick 是取部分值

type Pick<T,K extends keyof T> = {
    [P in K] : T[P]
}

interface Info {
    name:string,
    age:number,
    address:string
}

let info2:Info = {
    name:'xiao ming',
    age:19,
    address:'cahng sha'
}

function pick<T , K extends keyof T>(obj:T , keys:K[]):Pick<T , K>{ //Pick 是取部分
    const res:any = {};
    keys.map(item=>res[item] = obj[item])
    return res
}


const addname = pick(info2,['name','address'])

console.log(addname)

Record 这个就是将传入的属性转换为其他值得一种方法

function mapObject<K extends string | number,T ,U>(obj:Record<K,T>,f:(x:T)=>U):Record<K,U>{
    const res:any = {};
    for(const key in obj){
        res[key] = obj[key]
    }
    return res
}

const names = { 0:'hello',1:'world',2:'bye' }
const lengths = mapObject(names,s=>s.length)
console.log(lengths)

12.映射

一个常见的任务是将一个已知的类型每个属性都变为可选的:

interface PersonPartial {
    name?: string;
    age?: number;
}

或者我们想要一个只读版本:

interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}

这在JavaScript里经常出现,TypeScript提供了从旧类型中创建新类型的一种方式 — 映射类型。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。 例如,你可以令每个属性成为 readonly类型或可选的。 下面是一些例子:

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Partial<T> = {
    [P in keyof T]?: T[P];
}

像下面这样使用:

type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;

下面来看看最简单的映射类型和它的组成部分:

type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

它的语法与索引签名的语法类型,内部使用了 for … in。 具有三个部分:

类型变量 K,它会依次绑定到每个属性。
1.字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
2.属性的结果类型。
3.在个简单的例子里, Keys是硬编码的的属性名列表并且属性类型永远是 boolean,因此这个映射类型等同于:

type Flags = {
    option1: boolean;
    option2: boolean;
}

在真正的应用里,可能不同于上面的 Readonly或 Partial。 它们会基于一些已存在的类型,且按照一定的方式转换字段。 这就是 keyof和索引访问类型要做的事情:

type NullablePerson = { [P in keyof Person]: Person[P] | null }
type PartialPerson = { [P in keyof Person]?: Person[P] }

在这些例子里,属性列表是 keyof T且结果类型是 T[P]的变体。 这是使用通用映射类型的一个好模版。 因为这类转换是 同态的,映射只作用于 T的属性而没有其它的。 编译器知道在添加任何新属性之前可以拷贝所有存在的属性修饰符。 例如,假设 Person.name是只读的,那么 Partial.name也将是只读的且为可选的。

13.由映射类型进行推断

type Proxy<T> = {
    get():T;
    set(value:T):void;
}
type Proxify<T> = {
  [P in keyof T]:Proxy<T[P]>
}
function proxify<T>(obj:T):Proxify<T>{
  const result = {} as Proxify<T>
  for (const key in obj){
    result[key] = {
      get:() => obj[key],
      set:(value) => obj[key] = value
    }
  }
  return result
}


let props = {
  name:'lison',
  age:20,
}

let porxyProps = proxify(props)

// porxyProps.name.set('wang')
// console.log(porxyProps.name.get())
function unproxify<T>(t:Proxify<T>):T{
  const result = {} as T
  for(const key in t){
    result[key] = t[key].get()
  }
  return result
}

console.log(unproxify(porxyProps))

元组的映射

type MapToPromise<T> = {
  [P in keyof T] : Promise<T[P]>
}
type Tuple = [number,string,boolean]

type promiseTuple = MapToPromise<Tuple>
let tuple1:promiseTuple = [
  new Promise((resolve,reject) => resolve(1)),
  new Promise((resolve,reject) => resolve('aa')),
  new Promise((resolve,reject) => resolve(false))
]

14.unknown

14.1 unknown 是任何类型都可以赋值给他

let value1:unknown;
value1 = 1
value1 = 'as'

14.2 如果没有类型断言和基于控制细化时,unknown 是不可以赋值给任何类型的

let value2:unknown;
let value3:string = value2
value2 = value1 //对象的类型为 "unknown"

14.3 如果没有类型断言和基于控制流细化时,unknown 是不能在它上面进行任何的操作

let value4 : unknown
value4 += 5;  //对象的类型为 "unknown"

14.4 unknown 与任何类型组成的交叉类型都会等于其他类型。

type type1 = string & unknown
type type2 = number & unknown
type type3 = unknown & unknown
type type4 = string & string[]

14.5 unknown 与任何类型组成联合类型都会等于 unknown

type type5 = unknown | string 
type type6 = unknown | number 
type type7 = unknown | any

14.6 nerve 是 unknown的子类型

type type8 = never extends unknown ? true : false

14.7 kerof unknown 等于nerve

 type type9 = keyof unknown

14.8 自能对unknown 进行等于和不等于操作,其他的操作是不可以的

value1 === value2
value1 !== value2

14.9 unknown类型的值是不能访问他的属性,作为函数调用和作为实列来声明

let value5:unknown
value5.age
value5()
new value5()

14.10 如果使用映射来遍历unknown ,是不会返回任何的值

type Types1<T> = {
  [P in keyof T]:number
}

type type10 = Types1<number>
type type11 = Types1<unknown>

15.条件类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值