diff --git a/packages/solid/store/src/store.ts b/packages/solid/store/src/store.ts index c055f28d5..579c2053c 100644 --- a/packages/solid/store/src/store.ts +++ b/packages/solid/store/src/store.ts @@ -297,107 +297,117 @@ export type StoreSetter = export type Part = KeyOf> = | K | ([K] extends [never] ? never : readonly K[]) - | ([T] extends [readonly unknown[]] ? ArrayFilterFn | StorePathRange : never); + | (T extends readonly unknown[] ? ArrayFilterFn | StorePathRange : never); // shortcut to avoid writing `Exclude` too many times type W = Exclude; +// access only instances of T where K is keyof T, A for Access +type A = T extends unknown ? T[K & keyof T] : never; + // specially handle keyof to avoid errors with arrays and any -type KeyOf = number extends keyof T // have to check this otherwise ts won't allow KeyOf to index T - ? 0 extends 1 & T // if it's any just return keyof T - ? keyof T - : [T] extends [readonly unknown[]] - ? number // it's an array or tuple; exclude the non-number properties - : [T] extends [never] - ? never // keyof never is PropertyKey which number extends; return never - : keyof T // it's something which contains an index signature for strings or numbers - : keyof T; +type KeyOf = T extends unknown // distribute T in case it is a union + ? keyof T & + (0 extends 1 & T + ? unknown // don't narrow further than keyof T if it is any + : [T] extends [never] + ? never // keyof never is PropertyKey instead of never: return never + : [T] extends [readonly unknown[]] + ? number | `${number}` // extract numbers if T is an array + : unknown) // don't narrow further if T is something else + : never; type Rest = | [StoreSetter] | (0 extends 1 & T ? [...Part[], StoreSetter] - : DistributeRest, KeyOf>, U>); -// need a second type to distribute `K` -type DistributeRest = [T] extends [never] - ? never - : K extends KeyOf - ? [Part, ...Rest] - : never; + : T extends NotWrappable + ? never + : { [K in KeyOf]: [Part, ...Rest] }[KeyOf]); export interface SetStoreFunction { < K1 extends KeyOf>, - K2 extends KeyOf[K1]>>, - K3 extends KeyOf[K1]>[K2]>>, - K4 extends KeyOf[K1]>[K2]>[K3]>>, - K5 extends KeyOf[K1]>[K2]>[K3]>[K4]>>, - K6 extends KeyOf[K1]>[K2]>[K3]>[K4]>[K5]>>, - K7 extends KeyOf[K1]>[K2]>[K3]>[K4]>[K5]>[K6]>> + K2 extends KeyOf, K1>>>, + K3 extends KeyOf, K1>>, K2>>>, + K4 extends KeyOf, K1>>, K2>>, K3>>>, + K5 extends KeyOf, K1>>, K2>>, K3>>, K4>>>, + K6 extends KeyOf, K1>>, K2>>, K3>>, K4>>, K5>>>, + K7 extends KeyOf, K1>>, K2>>, K3>>, K4>>, K5>>, K6>>> >( k1: Part, K1>, - k2: Part[K1]>, K2>, - k3: Part[K1]>[K2]>, K3>, - k4: Part[K1]>[K2]>[K3]>, K4>, - k5: Part[K1]>[K2]>[K3]>[K4]>, K5>, - k6: Part[K1]>[K2]>[K3]>[K4]>[K5]>, K6>, - k7: Part[K1]>[K2]>[K3]>[K4]>[K5]>[K6]>, K7>, - ...rest: Rest[K1]>[K2]>[K3]>[K4]>[K5]>[K6]>[K7], [K7, K6, K5, K4, K3, K2, K1]> + k2: Part, K1>>, K2>, + k3: Part, K1>>, K2>>, K3>, + k4: Part, K1>>, K2>>, K3>>, K4>, + k5: Part, K1>>, K2>>, K3>>, K4>>, K5>, + k6: Part, K1>>, K2>>, K3>>, K4>>, K5>>, K6>, + k7: Part, K1>>, K2>>, K3>>, K4>>, K5>>, K6>>, K7>, + ...rest: Rest< + A, K1>>, K2>>, K3>>, K4>>, K5>>, K6>>, K7>, + [K7, K6, K5, K4, K3, K2, K1] + > ): void; < K1 extends KeyOf>, - K2 extends KeyOf[K1]>>, - K3 extends KeyOf[K1]>[K2]>>, - K4 extends KeyOf[K1]>[K2]>[K3]>>, - K5 extends KeyOf[K1]>[K2]>[K3]>[K4]>>, - K6 extends KeyOf[K1]>[K2]>[K3]>[K4]>[K5]>> + K2 extends KeyOf, K1>>>, + K3 extends KeyOf, K1>>, K2>>>, + K4 extends KeyOf, K1>>, K2>>, K3>>>, + K5 extends KeyOf, K1>>, K2>>, K3>>, K4>>>, + K6 extends KeyOf, K1>>, K2>>, K3>>, K4>>, K5>>> >( k1: Part, K1>, - k2: Part[K1]>, K2>, - k3: Part[K1]>[K2]>, K3>, - k4: Part[K1]>[K2]>[K3]>, K4>, - k5: Part[K1]>[K2]>[K3]>[K4]>, K5>, - k6: Part[K1]>[K2]>[K3]>[K4]>[K5]>, K6>, - setter: StoreSetter[K1]>[K2]>[K3]>[K4]>[K5]>[K6], [K6, K5, K4, K3, K2, K1]> + k2: Part, K1>>, K2>, + k3: Part, K1>>, K2>>, K3>, + k4: Part, K1>>, K2>>, K3>>, K4>, + k5: Part, K1>>, K2>>, K3>>, K4>>, K5>, + k6: Part, K1>>, K2>>, K3>>, K4>>, K5>>, K6>, + setter: StoreSetter< + A, K1>>, K2>>, K3>>, K4>>, K5>>, K6>, + [K6, K5, K4, K3, K2, K1] + > ): void; < K1 extends KeyOf>, - K2 extends KeyOf[K1]>>, - K3 extends KeyOf[K1]>[K2]>>, - K4 extends KeyOf[K1]>[K2]>[K3]>>, - K5 extends KeyOf[K1]>[K2]>[K3]>[K4]>> + K2 extends KeyOf, K1>>>, + K3 extends KeyOf, K1>>, K2>>>, + K4 extends KeyOf, K1>>, K2>>, K3>>>, + K5 extends KeyOf, K1>>, K2>>, K3>>, K4>>> >( k1: Part, K1>, - k2: Part[K1]>, K2>, - k3: Part[K1]>[K2]>, K3>, - k4: Part[K1]>[K2]>[K3]>, K4>, - k5: Part[K1]>[K2]>[K3]>[K4]>, K5>, - setter: StoreSetter[K1]>[K2]>[K3]>[K4]>[K5], [K5, K4, K3, K2, K1]> + k2: Part, K1>>, K2>, + k3: Part, K1>>, K2>>, K3>, + k4: Part, K1>>, K2>>, K3>>, K4>, + k5: Part, K1>>, K2>>, K3>>, K4>>, K5>, + setter: StoreSetter, K1>>, K2>>, K3>>, K4>>, K5>, [K5, K4, K3, K2, K1]> ): void; < K1 extends KeyOf>, - K2 extends KeyOf[K1]>>, - K3 extends KeyOf[K1]>[K2]>>, - K4 extends KeyOf[K1]>[K2]>[K3]>> + K2 extends KeyOf, K1>>>, + K3 extends KeyOf, K1>>, K2>>>, + K4 extends KeyOf, K1>>, K2>>, K3>>> >( k1: Part, K1>, - k2: Part[K1]>, K2>, - k3: Part[K1]>[K2]>, K3>, - k4: Part[K1]>[K2]>[K3]>, K4>, - setter: StoreSetter[K1]>[K2]>[K3]>[K4], [K4, K3, K2, K1]> + k2: Part, K1>>, K2>, + k3: Part, K1>>, K2>>, K3>, + k4: Part, K1>>, K2>>, K3>>, K4>, + setter: StoreSetter, K1>>, K2>>, K3>>, K4>, [K4, K3, K2, K1]> ): void; - >, K2 extends KeyOf[K1]>>, K3 extends KeyOf[K1]>[K2]>>>( + < + K1 extends KeyOf>, + K2 extends KeyOf, K1>>>, + K3 extends KeyOf, K1>>, K2>>> + >( k1: Part, K1>, - k2: Part[K1]>, K2>, - k3: Part[K1]>[K2]>, K3>, - setter: StoreSetter[K1]>[K2]>[K3], [K3, K2, K1]> + k2: Part, K1>>, K2>, + k3: Part, K1>>, K2>>, K3>, + setter: StoreSetter, K1>>, K2>>, K3>, [K3, K2, K1]> ): void; - >, K2 extends KeyOf[K1]>>>( + >, K2 extends KeyOf, K1>>>>( k1: Part, K1>, - k2: Part[K1]>, K2>, - setter: StoreSetter[K1]>[K2], [K2, K1]> + k2: Part, K1>>, K2>, + setter: StoreSetter, K1>>, K2>, [K2, K1]> ): void; - >>(k1: Part, K1>, setter: StoreSetter[K1], [K1]>): void; + >>(k1: Part, K1>, setter: StoreSetter, K1>, [K1]>): void; (setter: StoreSetter): void; } diff --git a/packages/solid/store/test/store.spec.ts b/packages/solid/store/test/store.spec.ts index ee8e99009..fd2fa0c3f 100644 --- a/packages/solid/store/test/store.spec.ts +++ b/packages/solid/store/test/store.spec.ts @@ -714,7 +714,7 @@ describe("Nested Classes", () => { // type tests -// NotWrappable keys are ignored +// setter ignores NotWrappable keys (unsafe) () => { const [, setStore] = createStore<{ a?: @@ -726,6 +726,29 @@ describe("Nested Classes", () => { setStore("a", "b", "c", "d", "e", "f", "g", "h"); }; +// setter can set type unions as if it were a member (unsafe) +() => { + interface Game { + name: string; + } + interface Boardgame extends Game { + type: "boardgame"; + nested: { pieces: number }; + } + interface VideoGame extends Game { + type: "videogame"; + otherNested: { platform: "Sony" | "Mircosoft" | "Nintendo" }; + } + type GameUnion = Boardgame | VideoGame; + const [games, setGames] = createStore([ + { name: "Settlers", nested: { pieces: 240 } } as Boardgame, + { name: "Zelda", otherNested: { platform: "Nintendo" } } as VideoGame + ]); + + games[0].type === "boardgame" && games[0].nested.pieces; + setGames(0, "nested", "pieces", Math.floor(Math.random() * 100)); +}; + // keys are narrowed () => { const [store, setStore] = createStore({ a: { b: 1 }, c: { d: 2 } });