-
Notifications
You must be signed in to change notification settings - Fork 1
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
StoreName type check not depends on infer #10
Changes from 5 commits
691cb76
ce4b2f7
e6b98a1
5558c02
ffc4b8a
f5eb191
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,25 @@ | ||
import { LocationStateContext } from "./context"; | ||
import { StoreName } from "./types"; | ||
import { DefaultStoreNames } from "./types"; | ||
import { useCallback, useContext, useState, useSyncExternalStore } from "react"; | ||
|
||
export type Refine<T> = (value: unknown) => T | undefined; | ||
|
||
export type LocationStateDefinition<T> = { | ||
export type LocationStateDefinition< | ||
T, | ||
StoreName extends string = DefaultStoreNames, | ||
> = { | ||
name: string; | ||
defaultValue: T; | ||
storeName: StoreName | string; | ||
// Avoid inference to rigorously check type arguments | ||
storeName: StoreName extends infer S ? S : never; | ||
refine?: Refine<T>; | ||
}; | ||
|
||
type Updater<T> = (prev: T) => T; | ||
type UpdaterOrValue<T> = T | Updater<T>; | ||
type SetState<T> = (updaterOrValue: UpdaterOrValue<T>) => void; | ||
|
||
const useStore = (storeName: StoreName | string) => { | ||
const useStore = (storeName: string) => { | ||
const { stores } = useContext(LocationStateContext); | ||
const store = stores[storeName]; | ||
if (!store) { | ||
|
@@ -25,16 +29,19 @@ const useStore = (storeName: StoreName | string) => { | |
return store; | ||
}; | ||
|
||
export const useLocationState = <T>( | ||
definition: LocationStateDefinition<T>, | ||
const _useLocationState = <T, StoreName extends string = DefaultStoreNames>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 型引数のデフォルトも不要かなと |
||
definition: LocationStateDefinition<T, StoreName>, | ||
): [T, SetState<T>] => { | ||
const storeState = useLocationStateValue(definition); | ||
const setStoreState = useLocationSetState(definition); | ||
const storeState = _useLocationStateValue(definition); | ||
const setStoreState = _useLocationSetState(definition); | ||
return [storeState, setStoreState]; | ||
}; | ||
|
||
export const useLocationStateValue = <T>( | ||
definition: LocationStateDefinition<T>, | ||
const _useLocationStateValue = < | ||
T, | ||
StoreName extends string = DefaultStoreNames, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同上 |
||
>( | ||
definition: LocationStateDefinition<T, StoreName>, | ||
): T => { | ||
const { name, defaultValue, storeName, refine } = useState(definition)[0]; | ||
const store = useStore(storeName); | ||
|
@@ -56,8 +63,8 @@ export const useLocationStateValue = <T>( | |
return storeState; | ||
}; | ||
|
||
export const useLocationSetState = <T>( | ||
definition: LocationStateDefinition<T>, | ||
const _useLocationSetState = <T, StoreName extends string = DefaultStoreNames>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同上 |
||
definition: LocationStateDefinition<T, StoreName>, | ||
): SetState<T> => { | ||
const { name, defaultValue, storeName, refine } = useState(definition)[0]; | ||
const store = useStore(storeName); | ||
|
@@ -78,3 +85,23 @@ export const useLocationSetState = <T>( | |
); | ||
return setStoreState; | ||
}; | ||
|
||
export const getHooksWith = <StoreNames extends string>() => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 自分のせいだけど |
||
({ | ||
useLocationState: _useLocationState, | ||
useLocationStateValue: _useLocationStateValue, | ||
useLocationSetState: _useLocationSetState, | ||
} as { | ||
useLocationState: <T>( | ||
definition: LocationStateDefinition<T, StoreNames>, | ||
) => [T, SetState<T>]; | ||
useLocationStateValue: <T>( | ||
definition: LocationStateDefinition<T, StoreNames>, | ||
) => T; | ||
useLocationSetState: <T>( | ||
definition: LocationStateDefinition<T, StoreNames>, | ||
) => SetState<T>; | ||
}); | ||
|
||
export const { useLocationState, useLocationStateValue, useLocationSetState } = | ||
getHooksWith<DefaultStoreNames>(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/// <reference types="navigation-api-types" /> | ||
|
||
export type StoreName = "session" | "url"; | ||
export type DefaultStoreNames = "session" | "url"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ということで |
||
|
||
export type Syncer = { | ||
key(): string | undefined; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これがうまくいくには
StoreName
のデフォルトがQiitaの記事のようにnever
でないとダメなのでは?string extends infer S
だとS
はstring
をキャプチャしてしまうからnever
にはならないと思われThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LocationStateDefinition
単体ではこれで推論されることはなくなる+明示的に型引数で指定するとうまくいってました。Qiitaの記事は型引数を必ず指定することを目的として推論させない+neverデフォルトなアプローチであって、推論されることを防ぐのみなら
StoreName extends infer S ? S : never;
でOKだからうまくいったんだと思ってました。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LocationStateDefinition
単体だとstoreName: StoreName
でもエラーになっていたはずつまり型変数
StoreName
が普通にDefaultStoreName(s)
になるんでしょう関数の型推論が特別に強力なんだろうなぁ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
あ、確かにでした。
関数の推論を
StoreName extends infer S ? S : never;
で防げるからうまくいくってところですかね...?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
この修正もう不要ですよね?
storeName: StoreName
でいいはず