Skip to content

Commit

Permalink
Merge branch 'main' into fix/slow-types
Browse files Browse the repository at this point in the history
  • Loading branch information
dai-shi authored Mar 29, 2024
2 parents c341e78 + 42f324c commit cf8f63a
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 96 deletions.
6 changes: 4 additions & 2 deletions src/react/useAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ export function useAtom<Value>(
options?: Options,
): [Awaited<Value>, never]

export function useAtom<AtomType extends WritableAtom<any, any[], any>>(
export function useAtom<
AtomType extends WritableAtom<unknown, never[], unknown>,
>(
atom: AtomType,
options?: Options,
): [
Awaited<ExtractAtomValue<AtomType>>,
SetAtom<ExtractAtomArgs<AtomType>, ExtractAtomResult<AtomType>>,
]

export function useAtom<AtomType extends Atom<any>>(
export function useAtom<AtomType extends Atom<unknown>>(
atom: AtomType,
options?: Options,
): [Awaited<ExtractAtomValue<AtomType>>, never]
Expand Down
2 changes: 1 addition & 1 deletion src/react/useAtomValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function useAtomValue<Value>(
options?: Options,
): Awaited<Value>

export function useAtomValue<AtomType extends Atom<any>>(
export function useAtomValue<AtomType extends Atom<unknown>>(
atom: AtomType,
options?: Options,
): Awaited<ExtractAtomValue<AtomType>>
Expand Down
4 changes: 3 additions & 1 deletion src/react/useSetAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export function useSetAtom<Value, Args extends unknown[], Result>(
options?: Options,
): SetAtom<Args, Result>

export function useSetAtom<AtomType extends WritableAtom<any, any[], any>>(
export function useSetAtom<
AtomType extends WritableAtom<unknown, never[], unknown>,
>(
atom: AtomType,
options?: Options,
): SetAtom<ExtractAtomArgs<AtomType>, ExtractAtomResult<AtomType>>
Expand Down
6 changes: 3 additions & 3 deletions src/react/utils/useHydrateAtoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ type Store = ReturnType<typeof useStore>
type Options = Parameters<typeof useStore>[0] & {
dangerouslyForceHydrate?: boolean
}
type AnyWritableAtom = WritableAtom<unknown, any[], any>
type AnyWritableAtom = WritableAtom<unknown, never[], unknown>

type InferAtomTuples<T> = {
[K in keyof T]: T[K] extends readonly [infer A, unknown]
? A extends WritableAtom<unknown, infer Args, any>
? A extends WritableAtom<unknown, infer Args, infer _Result>
? readonly [A, Args[0]]
: T[K]
: never
Expand Down Expand Up @@ -43,7 +43,7 @@ export function useHydrateAtoms<
for (const [atom, value] of values) {
if (!hydratedSet.has(atom) || options?.dangerouslyForceHydrate) {
hydratedSet.add(atom)
store.set(atom, value)
store.set(atom, value as never)
}
}
}
Expand Down
103 changes: 38 additions & 65 deletions src/vanilla/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ export const createStore = (): Store => {
},
}
try {
const valueOrPromise = atom.read(getter, options as any)
const valueOrPromise = atom.read(getter, options as never)
return setAtomValueOrPromise(atom, valueOrPromise, nextDependencies, () =>
controller?.abort(),
)
Expand All @@ -487,26 +487,6 @@ export const createStore = (): Store => {
const readAtom = <Value>(atom: Atom<Value>): Value =>
returnAtomValue(readAtomState(atom))

const addAtom = (atom: AnyAtom): Mounted => {
let mounted = mountedMap.get(atom)
if (!mounted) {
mounted = mountAtom(atom)
}
return mounted
}

// FIXME doesn't work with mutually dependent atoms
const canUnmountAtom = (atom: AnyAtom, mounted: Mounted) =>
!mounted.l.size &&
(!mounted.t.size || (mounted.t.size === 1 && mounted.t.has(atom)))

const delAtom = (atom: AnyAtom): void => {
const mounted = mountedMap.get(atom)
if (mounted && canUnmountAtom(atom, mounted)) {
unmountAtom(atom)
}
}

const recomputeDependents = (atom: AnyAtom): void => {
const getDependents = (a: AnyAtom): Dependents => {
const dependents = new Set(mountedMap.get(a)?.t)
Expand Down Expand Up @@ -629,16 +609,19 @@ export const createStore = (): Store => {
initialDependent?: AnyAtom,
onMountQueue?: (() => void)[],
): Mounted => {
const existingMount = mountedMap.get(atom)
if (existingMount) {
if (initialDependent) {
existingMount.t.add(initialDependent)
}
return existingMount
}

const queue = onMountQueue || []
// mount dependencies before mounting self
getAtomState(atom)?.d.forEach((_, a) => {
const aMounted = mountedMap.get(a)
if (aMounted) {
aMounted.t.add(atom) // add dependent
} else {
if (a !== atom) {
mountAtom(a, atom, queue)
}
if (a !== atom) {
mountAtom(a, atom, queue)
}
})
// recompute atom state
Expand Down Expand Up @@ -668,9 +651,17 @@ export const createStore = (): Store => {
return mounted
}

const unmountAtom = <Value>(atom: Atom<Value>): void => {
// FIXME doesn't work with mutually dependent atoms
const canUnmountAtom = (atom: AnyAtom, mounted: Mounted) =>
!mounted.l.size &&
(!mounted.t.size || (mounted.t.size === 1 && mounted.t.has(atom)))

const tryUnmountAtom = <Value>(atom: Atom<Value>, mounted: Mounted): void => {
if (!canUnmountAtom(atom, mounted)) {
return
}
// unmount self
const onUnmount = mountedMap.get(atom)?.u
const onUnmount = mounted.u
if (onUnmount) {
onUnmount()
}
Expand All @@ -687,12 +678,10 @@ export const createStore = (): Store => {
}
atomState.d.forEach((_, a) => {
if (a !== atom) {
const mounted = mountedMap.get(a)
if (mounted) {
mounted.t.delete(atom)
if (canUnmountAtom(a, mounted)) {
unmountAtom(a)
}
const mountedDep = mountedMap.get(a)
if (mountedDep) {
mountedDep.t.delete(atom)
tryUnmountAtom(a, mountedDep)
}
}
})
Expand Down Expand Up @@ -721,20 +710,12 @@ export const createStore = (): Store => {
}
})
depSet.forEach((a) => {
const mounted = mountedMap.get(a)
if (mounted) {
mounted.t.add(atom) // add to dependents
} else if (mountedMap.has(atom)) {
// we mount dependencies only when atom is already mounted
// Note: we should revisit this when you find other issues
// https://github.com/pmndrs/jotai/issues/942
mountAtom(a, atom)
}
mountAtom(a, atom)
})
maybeUnmountAtomSet.forEach((a) => {
const mounted = mountedMap.get(a)
if (mounted && canUnmountAtom(a, mounted)) {
unmountAtom(a)
if (mounted) {
tryUnmountAtom(a, mounted)
}
})
}
Expand Down Expand Up @@ -798,7 +779,7 @@ export const createStore = (): Store => {
}

const subscribeAtom = (atom: AnyAtom, listener: () => void) => {
const mounted = addAtom(atom)
const mounted = mountAtom(atom)
const flushed = flushPending([atom])
const listeners = mounted.l
listeners.add(listener)
Expand All @@ -809,7 +790,7 @@ export const createStore = (): Store => {
}
return () => {
listeners.delete(listener)
delAtom(atom)
tryUnmountAtom(atom, mounted)
if (import.meta.env?.MODE !== 'production') {
// devtools uses this to detect if it _can_ unmount or not
storeListenersRev2.forEach((l) => l({ type: 'unsub' }))
Expand Down Expand Up @@ -859,25 +840,17 @@ export const createStore = (): Store => {

let defaultStore: Store | undefined

if (import.meta.env?.MODE !== 'production') {
if (typeof (globalThis as any).__NUMBER_OF_JOTAI_INSTANCES__ === 'number') {
++(globalThis as any).__NUMBER_OF_JOTAI_INSTANCES__
} else {
;(globalThis as any).__NUMBER_OF_JOTAI_INSTANCES__ = 1
}
}

export const getDefaultStore = (): Store => {
if (!defaultStore) {
if (
import.meta.env?.MODE !== 'production' &&
(globalThis as any).__NUMBER_OF_JOTAI_INSTANCES__ !== 1
) {
console.warn(
'Detected multiple Jotai instances. It may cause unexpected behavior with the default store. https://github.com/pmndrs/jotai/discussions/2044',
)
}
defaultStore = createStore()
if (import.meta.env?.MODE !== 'production') {
;(globalThis as any).__JOTAI_DEFAULT_STORE__ ||= defaultStore
if ((globalThis as any).__JOTAI_DEFAULT_STORE__ !== defaultStore) {
console.warn(
'Detected multiple Jotai instances. It may cause unexpected behavior with the default store. https://github.com/pmndrs/jotai/discussions/2044',
)
}
}
}
return defaultStore
}
8 changes: 6 additions & 2 deletions src/vanilla/typeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ export type ExtractAtomValue<AtomType> =
AtomType extends Atom<infer Value> ? Value : never

export type ExtractAtomArgs<AtomType> =
AtomType extends WritableAtom<any, infer Args, any> ? Args : never
AtomType extends WritableAtom<unknown, infer Args, infer _Result>
? Args
: never

export type ExtractAtomResult<AtomType> =
AtomType extends WritableAtom<any, any[], infer Result> ? Result : never
AtomType extends WritableAtom<unknown, infer _Args, infer Result>
? Result
: never

export type SetStateAction<Value> = ExtractAtomArgs<PrimitiveAtom<Value>>[0]
2 changes: 1 addition & 1 deletion src/vanilla/utils/atomWithReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function atomWithReducer<Value, Action>(
initialValue: Value,
reducer: (value: Value, action: Action) => Value,
) {
return atom(initialValue, function (this: any, get, set, action: Action) {
return atom(initialValue, function (this: never, get, set, action: Action) {
set(this, reducer(get(this), action))
})
}
2 changes: 1 addition & 1 deletion src/vanilla/utils/atomWithRefresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function atomWithRefresh<Value, Args extends unknown[], Result>(
return atom(
(get, options) => {
get(refreshAtom)
return read(get, options as any)
return read(get, options as never)
},
(get, set, ...args: Args) => {
if (args.length === 0) {
Expand Down
12 changes: 6 additions & 6 deletions src/vanilla/utils/atomWithStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function withStorageValidator<Value>(
return validate(value)
},
}
return storage as any // FIXME better way to type this?
return storage
}
}

Expand Down Expand Up @@ -113,7 +113,7 @@ export function createJSONStorage<Value>(
options?: JsonStorageOptions,
): AsyncStorage<Value> | SyncStorage<Value> {
let lastStr: string | undefined
let lastValue: any
let lastValue: Value
const storage: AsyncStorage<Value> | SyncStorage<Value> = {
getItem: (key, initialValue) => {
const parse = (str: string | null) => {
Expand All @@ -130,9 +130,9 @@ export function createJSONStorage<Value>(
}
const str = getStringStorage()?.getItem(key) ?? null
if (isPromiseLike(str)) {
return str.then(parse)
return str.then(parse) as never
}
return parse(str)
return parse(str) as never
},
setItem: (key, newValue) =>
getStringStorage()?.setItem(
Expand Down Expand Up @@ -197,7 +197,7 @@ export function atomWithStorage<Value>(
| SyncStorage<Value>
| AsyncStorage<Value> = defaultStorage as SyncStorage<Value>,
options?: { getOnInit?: boolean },
): any {
) {
const getOnInit = options?.getOnInit
const baseAtom = atom(
getOnInit
Expand Down Expand Up @@ -244,5 +244,5 @@ export function atomWithStorage<Value>(
},
)

return anAtom
return anAtom as never
}
16 changes: 8 additions & 8 deletions src/vanilla/utils/freezeAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,33 @@ const cache1 = new WeakMap()
const memo1 = <T>(create: () => T, dep1: object): T =>
(cache1.has(dep1) ? cache1 : cache1.set(dep1, create())).get(dep1)

const deepFreeze = (obj: any) => {
const deepFreeze = (obj: unknown) => {
if (typeof obj !== 'object' || obj === null) return
Object.freeze(obj)
const propNames = Object.getOwnPropertyNames(obj)
for (const name of propNames) {
const value = obj[name]
const value = (obj as never)[name]
deepFreeze(value)
}
return obj
}

export function freezeAtom<AtomType extends Atom<any>>(
export function freezeAtom<AtomType extends Atom<unknown>>(
anAtom: AtomType,
): AtomType {
return memo1(() => {
const frozenAtom: any = atom(
const frozenAtom = atom(
(get) => deepFreeze(get(anAtom)),
(_get, set, arg) => set(anAtom as any, arg),
(_get, set, arg) => set(anAtom as never, arg),
)
return frozenAtom
return frozenAtom as never
}, anAtom)
}

export function freezeAtomCreator<
CreateAtom extends (...params: any[]) => Atom<any>,
CreateAtom extends (...params: never[]) => Atom<unknown>,
>(createAtom: CreateAtom): CreateAtom {
return ((...params: any[]) => {
return ((...params: never[]) => {
const anAtom = createAtom(...params)
const origRead = anAtom.read
anAtom.read = function (get, options) {
Expand Down
Loading

0 comments on commit cf8f63a

Please sign in to comment.