Ref的四种写法
这是一个ref:
const refA = ref();
什么都没有, 所以是一个Ref<any>
可以这样:
refA.value = 123;
refA.value = "123";
refA.value = ["123", 123];
这是另一个ref:
const refB = ref(1);
这时候, 他就是一个Ref<number>
, 所以:
refB.value = 2;
refB.value = -1;
refB.value = {}; // Error: Type '{}' is not assignable to type 'number'
这是第三个ref:
const refC = ref() as Ref<number>;
这时候, 因为as了, 所以他是一个Ref<number>
, 性质跟refB
类似, 除了一点:
他并不安全.refC
声明的时候并没有初始化值, 其类型本应该是Ref<any>
, 或者详细点说是Ref<any | undefined>
(any
包括undefined
, 所以合并成了any
)
所以可能会导致意外的类型错误, 慎用as
.
这是第四个ref:
const refD = ref<number>();
通过泛型, 限定了Ref的类型只能为number
, 但是由于未被初始化, 所以实际类型是Ref<number | undefined>
所以使用前必须判空, 安全起来了.
热身运动做完了, 接下来该做点体操了.
0x00:
const refE = ref([] as number[]);
const refF = ref([]);
结果是Ref<number[]>
和Ref<never[]>
.
因此:
refE.push(1);
refF.push(1); // Error
0x01:
const refG = ref(ref(ref(1)));
const refH = ref(reactive({name: ""}));
结果是Ref<number>
和Ref<{ name: string }>
, ref会对深层Ref解套.
0x02:
interface A {
name: string;
age?: number;
}
const refI = ref({ name: "Zapic" }) as Ref<A>;
const refJ = ref<A>({ name: "Zapic" });
const refK = ref({ name: "Zapic" });
结果是Ref<A>
/Ref<A>
和Ref<{ name: string }>
因此:
refI.age = 24;
refJ.age = 24;
refK.age = 24; // Error, age not exist on { name: string }
ref的类型声明
export declare function ref<T extends object>(value: T): ToRef<T>;
declare type ToRef<T> = [T] extends [Ref] ? T : Ref<UnwrapRef<T>>;
对于对象, 会直接调用reactive
变成响应式对象, 其属性也会被监控.
无论嵌套多少层, 最终结果仅会有一层Ref包装.
export declare function ref<T>(value: T): Ref<UnwrapRef<T>>;
对于在定义时初始化的ref, 其类型直接变为Ref<T>
, 同时保证仅有一层Ref包装.
export declare function ref<T = any>(): Ref<T | undefined>;
对于在定义时未初始化值的ref, 其类型默认为any. 若通过泛型参数指定了类型, 其最终类型也会包括一个undefined
.
UnwrapRef的类型声明
declare type BaseTypes = string | number | boolean;
declare type CollectionTypes = IterableCollections | WeakCollections;
export declare type UnwrapRef<T> = T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>;
declare type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref | RefUnwrapBailTypes[keyof RefUnwrapBailTypes] ? T : T extends Array<any> ? {
[K in keyof T]: UnwrapRefSimple<T[K]>;
} : T extends object ? {
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>;
} : T;
若传入UnwrapRef
的类型为Ref<V>
, 则提取出Ref<V>
的实际类型(V
), 传入UnwrapRefSimple
处理, 否则传入类型本身.
在UnwrapRefSimple<T>
内:
T
为Function
CollectionTypes
BaseTypes
Ref
RefUnwrapBailTypes
时返回其本身.
其中RefUnwrapBailTypes
是一个提供给外部插件的特殊接口, 用于指定哪些类型应当被跳过.
解Ref包裹的过程已经在Ref<infer V>
实现, 如果再次遇到Ref, 可能是对象内部属性的Ref, 不应当被处理.T
为Array
类型时, 其内部的所有元素都会被丢进UnwrapRefSimple
再次处理.T
为Object
类型时, 如果其内部某属性键类型为Symbol
, 则直接原样返回, 否则丢进UnwrapRef
内再次处理.