yushen7 / blog Goto Github PK
View Code? Open in Web Editor NEWGithub Pages个人博客仓库:https://yushen7.github.io/blog
Github Pages个人博客仓库:https://yushen7.github.io/blog
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 :
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.name
为Ref
类型,我们无法得知出value.name.value
的类型,换句话说我们需要从外部知道Ref<T>
中的这个T
的类型,这个T也就对应着value.name.value
的类型。
所以需要用到infer
,infer
可以用于提取函数的返回类型,如:
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练习:https://github.com/mdevils/typescript-exercises
此文是关于该练习的记录。
给定如下形式,要求定义合适的类型:
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
}
给定如下形式,要求定义合适的类型:
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}`);
}
User
和 Admin
有相同的字段,也有属于自己的字段。logPerson
只用到了它们相同的字段,所以可以用union type简单表示它们两者的结合~
https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types
代码很简单:
type Person = Admin | User
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.