Coder Social home page Coder Social logo

blog's Introduction

Hi there.

blog's People

Contributors

yushen7 avatar

Stargazers

 avatar

Watchers

 avatar

blog's Issues

Typescript之Vue中的Ref类型

Vue3里可以使用ref(val)返回一个Ref类型的数据,如:

const count = 1
const countRef = ref(count)

console.log(countRef.value) // 1
countRef.value = 2              

Ref类型:

type Ref<T = any> {
  value: T
}

对于简单类型的值(number/string/boolean)或函数(Function),可以写出如下代码:

function ref<T>(value: T): Ref<T>

而对于Ref类型的参数,则需使用条件类型(conditional type)判断,如:

function ref<T>(value: T) T extends Ref ? T : Ref<T>

这两种情况都好解决,关键是对于 object 或 Array 类型的参数:

  • object 的情况,递归遍历 object 的每个键,每个键值不能是 Ref 类型
  • Array 的情况,遍历 Array 的每个元素,元素保留自身的类型,如果元素是 object 或 Array ,需要再次进行遍历操作

首先是 object :

const value = {name: ref('jack')}
const vRef = ref(value)
typeof vRef.value.name        // Ref     × 
typeof vRef.value.name        // string √

对于 object 的情况,我们可以写出以下代码,用 keyof 遍历 object 属性:

type BaseTypes = number| string | boolean
type UnwrapObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } 

type UnwrapRef<T> =  T extends BaseTypes | Function ? T : T extends object ? UnwrapObject<T> : T

function ref<T>(value: T) T extends Ref ? T : Ref<UnwrapRef<T>>

我们将参数非Ref的情况移交给了UnwrapRef<T>这个条件类型处理。
UnwrapRef<T>BaseTypes Function类型返回,对于object则移交UnwrapObject<T>处理。
UnwrapObject<T>keyof遍历了参数,并对每个属性值同样移交UnwrapRef处理。

然鹅我们会发现,typeof vRef.value.name 依然是Ref类型。

因为如果value.nameRef类型,我们无法得知出value.name.value的类型,换句话说我们需要从外部知道Ref<T>中的这个T的类型,这个T也就对应着value.name.value的类型。

所以需要用到inferinfer可以用于提取函数的返回类型,如:

function add(x:number, y:number) {
  return x + y
}

type sum = typeof add extends (...args: any) => infer R ? R : any // type number

infer R在函数返回的位置,相当于一个占位符,代推断的类型,当条件判断结束后,可以使用这个类型。
infer的作用类似于在一个完整的结构(函数)里提取出某一个需要的部分(返回值类型),以供使用。

ts里有内置的ReturnType<T>类型,实现了一样的效果

所以利用infer,可以提取Ref<T>(完整的结构)中的value属性的类型(我们需要的部分)。

代码如下:

const refV = ref(1)
type InferValuePartType<T> =   T extends Ref<infer V> ? V : T

type valueType = InferValuePartType<typeof refV> // number

结合之前的代码:

type UnwrapRef<T> =  T extends Ref<infer V> ? V 
  : T extends BaseTypes | Function 
    ? T : T extends object ? UnwrapObject<T> : T

至此,完成对 object 的支持。

对于 Array 的情况,同样在UnwrapRef 加入一个判断 Array 类型的条件:

type UnwrapArray =  { [K in keyof T]: UnwrapRef<T[K]> }
type UnwrapRef<T> =  T extends Ref<infer V> ? V 
  : T extends BaseTypes | Function 
    ? T 
    : T extends object 
      ? UnwrapObject<T>
      : T extends Array<any>
         ? UnwrapArray<T> : T

看起来「万事大吉」。但别忘了,我们需要数组中的元素保持原先的类型,换句话说如果是这样的:[Ref, Ref],那么我们不需要元素被解成value的类型,保持Ref类型。

所以,UnwrapRef 的职能又变了,如下:

type UnwrapArr<T> = { [K in keyof T]: UnwrapRefSimple<T[K]> }
type UnwrapObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } 

type UnwrapRefSimple<T> = T extends 
    | BaseTypes 
    | Function
    | Ref
      ? T 
      : T extends Array<any> 
        ? UnwrapArr<T>
        : T extends object 
          ? UnwrapObject<T> : T


type UnwrapRef<T> =  T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>

多了一个UnwrapRefSimple,之前UnwrapRef的功能被拆成了两部分实现,一部分由UnwrapRefSimple实现,而infer提取的功能由UnwrapRef实现。

UnwrapRef先判断参数是否是Ref类型,如果是,则将推断后的V移交UnwrapRefSimple,否则将参数T移交UnwrapRefSimple

至此,实现了一开始所述情况。

完整代码如下:

/**
 * ref 函数始终返回一个类型为 Ref 的值
 * 
 * 1. 参数是简单值,直接返回
 * 2. 参数是一个Ref,用infer V获取参数类型,返回V
 * 3. 参数是一个object,遍历该object,将属性解成简单值
 * 4. 参数是Array,遍历Array,保留数组元素里的类型
 * 
 */


type Ref<T = any> = {

  value: T
}

type BaseTypes = string | number | boolean

type UnwrapArr<T> = { [K in keyof T]: UnwrapRefSimple<T[K]> }
type UnwrapObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } 

type UnwrapRefSimple<T> = T extends 
    | BaseTypes 
    | Function
    | Ref
      ? T 
      : T extends Array<any> 
        ? UnwrapArr<T>
        : T extends object 
          ? UnwrapObject<T> : T


type UnwrapRef<T> =  T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>

function ref<T>(value: T): T extends Ref ? T : Ref<UnwrapRef<T>>


const what = ref([1,2,3,4])

typescript练习记录

typescript练习:https://github.com/mdevils/typescript-exercises

此文是关于该练习的记录。

00

给定如下形式,要求定义合适的类型:

const users: unknown[] = [
    {
        name: 'Max Mustermann',
        age: 25,
        occupation: 'Chimney sweep'
    },
    {
        name: 'Kate Müller',
        age: 23,
        occupation: 'Astronaut'
    }
];

定义一个interface~:

interface User {
    name: string,
    age: number,
    occupation: string
}

01

给定如下形式,要求定义合适的类型:

interface User {
    name: string;
    age: number;
    occupation: string;
}

interface Admin {
    name: string;
    age: number;
    role: string;
}
const persons: Person[] /* <- Person[] */ = [
    {
        name: 'Max Mustermann',
        age: 25,
        occupation: 'Chimney sweep'
    },
    {
        name: 'Jane Doe',
        age: 32,
        role: 'Administrator'
    },
    {
        name: 'Kate Müller',
        age: 23,
        occupation: 'Astronaut'
    },
    {
        name: 'Bruce Willis',
        age: 64,
        role: 'World saver'
    }
];
function logPerson(user: Person) {
    console.log(` - ${chalk.green(user.name)}, ${user.age}`);
}

UserAdmin 有相同的字段,也有属于自己的字段。logPerson 只用到了它们相同的字段,所以可以用union type简单表示它们两者的结合~

https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types

代码很简单:

type Person = Admin | User

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.