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); // Error0x01:
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为FunctionCollectionTypesBaseTypesRefRefUnwrapBailTypes时返回其本身.
其中RefUnwrapBailTypes是一个提供给外部插件的特殊接口, 用于指定哪些类型应当被跳过.
解Ref包裹的过程已经在Ref<infer V>实现, 如果再次遇到Ref, 可能是对象内部属性的Ref, 不应当被处理.T为Array类型时, 其内部的所有元素都会被丢进UnwrapRefSimple再次处理.T为Object类型时, 如果其内部某属性键类型为Symbol, 则直接原样返回, 否则丢进UnwrapRef内再次处理.