Need help with dynamically typed rest parameters

type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

type LastOf<T> =
UnionToIntersection<T extends any ? () => T : never> extends () => (infer R) ? R : never;

type Merge<A, B> =
{ [K in keyof A]: K extends keyof B ? B[K] : A[K] } & B extends infer O ? { [K in Exclude<keyof O, keyof A>]: O[K] } : never;

type ValueOf<T> = T[keyof T];

type StateOf<T> = T extends StateSlice<any, infer S> ? S : never;

class StateSlice<T, K extends { [key: string]: T }> {
constructor(public state: K) {}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
mergeStates<T extends StateSlice<any, any>>(
...slices: T[]
): StateSlice<ValueOf<Merge<StateOf<LastOf<T>>, UnionToIntersection<StateOf<T>>>>, Merge<StateOf<LastOf<T>>, UnionToIntersection<StateOf<T>>>> {
const newState = Object.assign(
{},
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return
...slices.map((slice) => slice.state)
) as Merge<StateOf<LastOf<T>>, UnionToIntersection<StateOf<T>>>;
return new StateSlice(newState);
}


trim(keys: (keyof K)[]): StateSlice<T, Pick<K, (typeof keys)[number]>> {
const newState = keys.reduce(
(state, key) => ({ ...state, [key]: this.state[key] }),
{} as Pick<K, (typeof keys)[number]>
);
return new StateSlice(newState);
}

append<J extends string, V>(
key: J,
value: V
): StateSlice<T | V, K & { [P in J]: V }> {
return new StateSlice({ ...this.state, [key]: value });
}

split(
keys: (keyof K)[]
): [
StateSlice<T, Pick<K, (typeof keys)[number]>>,
StateSlice<T, Omit<K, (typeof keys)[number]>>
] {
const includedState = this.trim(keys).state;
const excludedState = Object.keys(this.state)
.filter((key) => !keys.includes(key as keyof K))
.reduce(
(state, key) => ({ ...state, [key]: this.state[key as keyof K] }),
{} as Omit<K, (typeof keys)[number]>
);
return [new StateSlice(includedState), new StateSlice(excludedState)];
}
}

function createStateSlice<T, K extends { [key: string]: T }>(
initialState: K
): StateSlice<T, K> {
return new StateSlice(initialState);
}

const UserSignIn = createStateSlice({
username: "",
password: "",
});

const UserPreferences = createStateSlice({
displayName: "",
email: "",
});

const UserPreferences2 = createStateSlice({
firstName: "",
lastName: "",
confirmPassword: "",
});

const UserSignUp = UserSignIn.mergeStates(UserPreferences, UserPreferences2);
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

type LastOf<T> =
UnionToIntersection<T extends any ? () => T : never> extends () => (infer R) ? R : never;

type Merge<A, B> =
{ [K in keyof A]: K extends keyof B ? B[K] : A[K] } & B extends infer O ? { [K in Exclude<keyof O, keyof A>]: O[K] } : never;

type ValueOf<T> = T[keyof T];

type StateOf<T> = T extends StateSlice<any, infer S> ? S : never;

class StateSlice<T, K extends { [key: string]: T }> {
constructor(public state: K) {}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
mergeStates<T extends StateSlice<any, any>>(
...slices: T[]
): StateSlice<ValueOf<Merge<StateOf<LastOf<T>>, UnionToIntersection<StateOf<T>>>>, Merge<StateOf<LastOf<T>>, UnionToIntersection<StateOf<T>>>> {
const newState = Object.assign(
{},
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return
...slices.map((slice) => slice.state)
) as Merge<StateOf<LastOf<T>>, UnionToIntersection<StateOf<T>>>;
return new StateSlice(newState);
}


trim(keys: (keyof K)[]): StateSlice<T, Pick<K, (typeof keys)[number]>> {
const newState = keys.reduce(
(state, key) => ({ ...state, [key]: this.state[key] }),
{} as Pick<K, (typeof keys)[number]>
);
return new StateSlice(newState);
}

append<J extends string, V>(
key: J,
value: V
): StateSlice<T | V, K & { [P in J]: V }> {
return new StateSlice({ ...this.state, [key]: value });
}

split(
keys: (keyof K)[]
): [
StateSlice<T, Pick<K, (typeof keys)[number]>>,
StateSlice<T, Omit<K, (typeof keys)[number]>>
] {
const includedState = this.trim(keys).state;
const excludedState = Object.keys(this.state)
.filter((key) => !keys.includes(key as keyof K))
.reduce(
(state, key) => ({ ...state, [key]: this.state[key as keyof K] }),
{} as Omit<K, (typeof keys)[number]>
);
return [new StateSlice(includedState), new StateSlice(excludedState)];
}
}

function createStateSlice<T, K extends { [key: string]: T }>(
initialState: K
): StateSlice<T, K> {
return new StateSlice(initialState);
}

const UserSignIn = createStateSlice({
username: "",
password: "",
});

const UserPreferences = createStateSlice({
displayName: "",
email: "",
});

const UserPreferences2 = createStateSlice({
firstName: "",
lastName: "",
confirmPassword: "",
});

const UserSignUp = UserSignIn.mergeStates(UserPreferences, UserPreferences2);
Argument of type 'StateSlice<unknown, { firstName: string; lastName: string; confirmPassword: string; }>' is not assignable to parameter of type 'StateSlice<unknown, { displayName: string; email: string; }>'.
Type '{ firstName: string; lastName: string; confirmPassword: string; }' is missing the following properties from type '{ displayName: string; email: string; }': displayName, emailts(2345)
const UserPreferences2: StateSlice<unknown, {
firstName: string;
lastName: string;
confirmPassword: string;
}>
Argument of type 'StateSlice<unknown, { firstName: string; lastName: string; confirmPassword: string; }>' is not assignable to parameter of type 'StateSlice<unknown, { displayName: string; email: string; }>'.
Type '{ firstName: string; lastName: string; confirmPassword: string; }' is missing the following properties from type '{ displayName: string; email: string; }': displayName, emailts(2345)
const UserPreferences2: StateSlice<unknown, {
firstName: string;
lastName: string;
confirmPassword: string;
}>
{ firstName: string; lastName: string; confirmPassword: string; } is not assignable to parameter of type { displayName: string; email: string; } It's basically telling me that UserPrefernces2 isnt the exact same as UserPreferences and therefore an error. Is there anyway for me to dynamicly type ...rest parameters without losing typesaftey or throwing an error?
0 Replies
No replies yetBe the first to reply to this messageJoin