Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(@toss/react): literal type inference #292

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react/react/src/hooks/useStorageState.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function createMockStorage() {
};
}

function createFixture<T extends Serializable>({ defaultValue }: { defaultValue?: T } = {}) {
function createFixture<T>({ defaultValue }: { defaultValue?: Serializable<T> } = {}) {
const key = '@@test-key';
const storage = createMockStorage();
const render = () => renderHook(() => useStorageState<T>(key, { storage, defaultValue }));
Expand Down
30 changes: 17 additions & 13 deletions packages/react/react/src/hooks/useStorageState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@
import { safeLocalStorage, Storage } from '@toss/storage';
import { SetStateAction, useCallback, useState } from 'react';

export type Serializable = string | number | boolean | unknown[] | Record<string, unknown>;
type ToPrimitive<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : never;
type ToObject<T> = T extends unknown[] | Record<string, unknown> ? T : never;

export type Serializable<T> = T extends string | number | boolean ? ToPrimitive<T> : ToObject<T>;

interface StorageStateOptions<T> {
storage?: Storage;
defaultValue?: T;
defaultValue?: Serializable<T>;
}

interface StorageStateOptionsWithDefaultValue<T> extends StorageStateOptions<T> {
defaultValue: T;
defaultValue: Serializable<T>;
}

/**
* 스토리지에 상태값을 저장하고 불러와서 값이 보존되는 useState로 동작하는 hook입니다.
* @param key 스토리지에 저장할 키
*/
export function useStorageState<T extends Serializable>(
export function useStorageState<T>(
key: string
): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void, () => void];
export function useStorageState<T extends Serializable>(
): readonly [Serializable<T> | undefined, (value: SetStateAction<Serializable<T> | undefined>) => void, () => void];
export function useStorageState<T>(
key: string,
{ storage, defaultValue }: StorageStateOptionsWithDefaultValue<T>
): readonly [T, (value: SetStateAction<T>) => void, () => void];
export function useStorageState<T extends Serializable>(
): readonly [Serializable<T>, (value: SetStateAction<Serializable<T>>) => void, () => void];
export function useStorageState<T>(
key: string,
{ storage, defaultValue }: StorageStateOptions<T>
): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void, () => void];
export function useStorageState<T extends Serializable>(
): readonly [Serializable<T> | undefined, (value: SetStateAction<Serializable<T> | undefined>) => void, () => void];
export function useStorageState<T>(
key: string,
{ storage = safeLocalStorage, defaultValue }: StorageStateOptions<T> = {}
): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void, () => void] {
): readonly [Serializable<T> | undefined, (value: SetStateAction<Serializable<T> | undefined>) => void, () => void] {
const getValue = useCallback(<T>() => {
const data = storage.get(key);

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

const [state, setState] = useState<T | undefined>(getValue);
const [state, setState] = useState<Serializable<T> | undefined>(getValue);

const set = useCallback(
(value: SetStateAction<T | undefined>) => {
(value: SetStateAction<Serializable<T> | undefined>) => {
setState(curr => {
const nextValue = typeof value === 'function' ? value(curr) : value;

Expand Down