Skip to content

Commit 858b428

Browse files
authored
fix(@toss/react): Improve literal type inference (#292)
1 parent 1d28bd5 commit 858b428

File tree

2 files changed

+18
-14
lines changed

2 files changed

+18
-14
lines changed

packages/react/react/src/hooks/useStorageState.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function createMockStorage() {
141141
};
142142
}
143143

144-
function createFixture<T extends Serializable>({ defaultValue }: { defaultValue?: T } = {}) {
144+
function createFixture<T>({ defaultValue }: { defaultValue?: Serializable<T> } = {}) {
145145
const key = '@@test-key';
146146
const storage = createMockStorage();
147147
const render = () => renderHook(() => useStorageState<T>(key, { storage, defaultValue }));

packages/react/react/src/hooks/useStorageState.ts

+17-13
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,39 @@
22
import { safeLocalStorage, Storage } from '@toss/storage';
33
import { SetStateAction, useCallback, useState } from 'react';
44

5-
export type Serializable = string | number | boolean | unknown[] | Record<string, unknown>;
5+
type ToPrimitive<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : never;
6+
type ToObject<T> = T extends unknown[] | Record<string, unknown> ? T : never;
7+
8+
export type Serializable<T> = T extends string | number | boolean ? ToPrimitive<T> : ToObject<T>;
9+
610
interface StorageStateOptions<T> {
711
storage?: Storage;
8-
defaultValue?: T;
12+
defaultValue?: Serializable<T>;
913
}
1014

1115
interface StorageStateOptionsWithDefaultValue<T> extends StorageStateOptions<T> {
12-
defaultValue: T;
16+
defaultValue: Serializable<T>;
1317
}
1418

1519
/**
1620
* 스토리지에 상태값을 저장하고 불러와서 값이 보존되는 useState로 동작하는 hook입니다.
1721
* @param key 스토리지에 저장할 키
1822
*/
19-
export function useStorageState<T extends Serializable>(
23+
export function useStorageState<T>(
2024
key: string
21-
): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void, () => void];
22-
export function useStorageState<T extends Serializable>(
25+
): readonly [Serializable<T> | undefined, (value: SetStateAction<Serializable<T> | undefined>) => void, () => void];
26+
export function useStorageState<T>(
2327
key: string,
2428
{ storage, defaultValue }: StorageStateOptionsWithDefaultValue<T>
25-
): readonly [T, (value: SetStateAction<T>) => void, () => void];
26-
export function useStorageState<T extends Serializable>(
29+
): readonly [Serializable<T>, (value: SetStateAction<Serializable<T>>) => void, () => void];
30+
export function useStorageState<T>(
2731
key: string,
2832
{ storage, defaultValue }: StorageStateOptions<T>
29-
): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void, () => void];
30-
export function useStorageState<T extends Serializable>(
33+
): readonly [Serializable<T> | undefined, (value: SetStateAction<Serializable<T> | undefined>) => void, () => void];
34+
export function useStorageState<T>(
3135
key: string,
3236
{ storage = safeLocalStorage, defaultValue }: StorageStateOptions<T> = {}
33-
): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void, () => void] {
37+
): readonly [Serializable<T> | undefined, (value: SetStateAction<Serializable<T> | undefined>) => void, () => void] {
3438
const getValue = useCallback(<T>() => {
3539
const data = storage.get(key);
3640

@@ -52,10 +56,10 @@ export function useStorageState<T extends Serializable>(
5256
}
5357
}, [defaultValue, key, storage]);
5458

55-
const [state, setState] = useState<T | undefined>(getValue);
59+
const [state, setState] = useState<Serializable<T> | undefined>(getValue);
5660

5761
const set = useCallback(
58-
(value: SetStateAction<T | undefined>) => {
62+
(value: SetStateAction<Serializable<T> | undefined>) => {
5963
setState(curr => {
6064
const nextValue = typeof value === 'function' ? value(curr) : value;
6165

0 commit comments

Comments
 (0)