Skip to content

Instantly share code, notes, and snippets.

@satouriko
Created December 1, 2023 01:32
Show Gist options
  • Save satouriko/155d4112890fa9b220d11b40dceca69c to your computer and use it in GitHub Desktop.
Save satouriko/155d4112890fa9b220d11b40dceca69c to your computer and use it in GitHub Desktop.
已经 mutable 还跟个傻逼一样假装单向数据流
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Ref, UnwrapNestedRefs, reactive } from 'vue';
import { ToComputedRefs, toComputedRefs } from './toComputedRefs';
type Model<T extends object, K extends object> = {
state: Ref<T>;
getters: {
[key in keyof K]: K[key] | Ref<K[key]>;
};
};
type UseModelFunction<T extends object, K extends object> = (...args: any) => Model<T, K>;
type ModelToComputedRefs<T extends UseModelFunction<object, object>> = ToComputedRefs<ReturnType<T>['state']> &
ReturnType<T>['getters'] & { model: Omit<ReturnType<T>, 'state' | 'getters'> };
export type ModelToProps<T extends UseModelFunction<object, object>> = UnwrapNestedRefs<ModelToComputedRefs<T>>;
export type ModelToModelKeys<T extends UseModelFunction<object, object>> = keyof ReturnType<T>['state']['value'] &
string;
export type ModelToEmits<T extends UseModelFunction<object, object>, ModelKeys extends ModelToModelKeys<T>> = {
<K extends ModelKeys>(e: `update:${K}`, value: ReturnType<T>['state']['value'][K]): void;
};
export function modelToAttrs<T extends object, K extends object>(
model: Model<T, K>,
vModel: Array<keyof T> = [],
): ModelToProps<UseModelFunction<T, K>> {
const { state, getters, ...rest } = model;
const states = toComputedRefs(state);
const listeners = vModel.reduce(
(obj, key) => ({
...obj,
[`onUpdate:${String(key)}`]: (val: (typeof states)[typeof key]) => {
states[key].value = val;
},
}),
{},
);
return reactive({
...states,
...getters,
...listeners,
model: rest,
});
}
/* eslint-disable no-param-reassign */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Ref, WritableComputedRef, computed, isRef } from 'vue';
function propertyToRef<T extends object>(object: T, key: string | symbol) {
return computed({
get() {
return isRef(object) ? object.value[key] : object[key];
},
set(val) {
if (isRef(object)) {
object.value[key] = val;
} else {
object[key] = val;
}
},
});
}
type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N;
export type ToComputedRef<T> = IfAny<T, WritableComputedRef<T>, [T] extends [Ref] ? T : WritableComputedRef<T>>;
export type ToComputedRefs<T = any> = T extends Ref
? {
[K in keyof T['value']]: ToComputedRef<T['value'][K]>;
}
: {
[K in keyof T]: ToComputedRef<T[K]>;
};
export function toComputedRefs<T extends object>(object: T): ToComputedRefs<T> {
const obj: any = isRef(object) ? object.value : object;
const ret: any = Array.isArray(obj) ? new Array(obj.length) : {};
for (const key in obj) {
ret[key] = propertyToRef(object, key);
}
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment