Skip to content

Commit

Permalink
fix(types): use TS interfaces only for public api (#499)
Browse files Browse the repository at this point in the history
  • Loading branch information
dai-shi committed Jul 19, 2022
1 parent 938894b commit f09d6c8
Show file tree
Hide file tree
Showing 8 changed files with 29 additions and 71 deletions.
1 change: 0 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/consistent-type-definitions": ["error", "interface"],
"jest/consistent-test-it": [
"error",
{ "fn": "it", "withinDescribe": "it" }
Expand Down
30 changes: 4 additions & 26 deletions src/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,10 @@ import {
// The following is a workaround until ESM is supported.
import useSyncExternalStoreExports from 'use-sync-external-store/shim'
import { snapshot, subscribe } from './vanilla'
import type { INTERNAL_AsRef } from './vanilla'
import type { INTERNAL_Snapshot } from './vanilla'

const { useSyncExternalStore } = useSyncExternalStoreExports

// Unfortunately, this doesn't work with tsc.
// Hope to find a solution to make this work.
//
// class SnapshotWrapper<T extends object> {
// fn(p: T) {
// return snapshot(p)
// }
// }
// type Snapshot<T extends object> = ReturnType<SnapshotWrapper<T>['fn']>
//
// Using copy-paste types for now:
type AnyFunction = (...args: any[]) => any
type Snapshot<T> = T extends AnyFunction
? T
: T extends INTERNAL_AsRef
? T
: T extends Promise<infer V>
? Snapshot<V>
: {
readonly [K in keyof T]: Snapshot<T[K]>
}

const useAffectedDebugValue = (
state: object,
affected: WeakMap<object, unknown>
Expand All @@ -47,7 +25,7 @@ const useAffectedDebugValue = (
useDebugValue(pathList.current)
}

interface Options {
type Options = {
sync?: boolean
}

Expand Down Expand Up @@ -126,9 +104,9 @@ interface Options {
export function useSnapshot<T extends object>(
proxyObject: T,
options?: Options
): Snapshot<T> {
): INTERNAL_Snapshot<T> {
const notifyInSync = options?.sync
const lastSnapshot = useRef<Snapshot<T>>()
const lastSnapshot = useRef<INTERNAL_Snapshot<T>>()
const lastAffected = useRef<WeakMap<object, unknown>>()
let inRender = true
const currSnapshot = useSyncExternalStore(
Expand Down
4 changes: 2 additions & 2 deletions src/utils/derive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getVersion, proxy, subscribe } from '../vanilla'

type DeriveGet = <T extends object>(proxyObject: T) => T

interface Subscription {
type Subscription = {
s: object // "s"ourceObject
d: object // "d"erivedObject
k: string // derived "k"ey
Expand Down Expand Up @@ -198,7 +198,7 @@ export function derive<T extends object, U extends object>(
throw new Error('object property already defined')
}
const fn = derivedFns[key as keyof U]
interface DependencyEntry {
type DependencyEntry = {
v: number // "v"ersion
s?: Subscription // "s"ubscription
}
Expand Down
4 changes: 2 additions & 2 deletions src/utils/devtools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { snapshot, subscribe } from '../vanilla'
import type {} from '@redux-devtools/extension'

// FIXME https://github.com/reduxjs/redux-devtools/issues/1097
interface Message {
type Message = {
type: string
payload?: any
state?: any
}

const DEVTOOLS = Symbol()

interface Options {
type Options = {
enabled?: boolean
name?: string
}
Expand Down
30 changes: 4 additions & 26 deletions src/utils/proxyWithComputed.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,5 @@
import { proxy, snapshot } from '../vanilla'
import type { INTERNAL_AsRef } from '../vanilla'

// Unfortunately, this doesn't work with tsc.
// Hope to find a solution to make this work.
//
// class SnapshotWrapper<T extends object> {
// fn(p: T) {
// return snapshot(p)
// }
// }
// type Snapshot<T extends object> = ReturnType<SnapshotWrapper<T>['fn']>
//
// Using copy-paste types for now:
type AnyFunction = (...args: any[]) => any
type Snapshot<T> = T extends AnyFunction
? T
: T extends INTERNAL_AsRef
? T
: T extends Promise<infer V>
? Snapshot<V>
: {
readonly [K in keyof T]: Snapshot<T[K]>
}
import type { INTERNAL_Snapshot } from '../vanilla'

/**
* proxyWithComputed
Expand Down Expand Up @@ -51,9 +29,9 @@ export function proxyWithComputed<T extends object, U extends object>(
initialObject: T,
computedFns: {
[K in keyof U]:
| ((snap: Snapshot<T>) => U[K])
| ((snap: INTERNAL_Snapshot<T>) => U[K])
| {
get: (snap: Snapshot<T>) => U[K]
get: (snap: INTERNAL_Snapshot<T>) => U[K]
set?: (state: T, newValue: U[K]) => void
}
}
Expand All @@ -66,7 +44,7 @@ export function proxyWithComputed<T extends object, U extends object>(
const { get, set } = (
typeof computedFn === 'function' ? { get: computedFn } : computedFn
) as {
get: (snap: Snapshot<T>) => U[typeof key]
get: (snap: INTERNAL_Snapshot<T>) => U[typeof key]
set?: (state: T, newValue: U[typeof key]) => void
}
const desc: PropertyDescriptor = {}
Expand Down
2 changes: 1 addition & 1 deletion src/utils/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { subscribe } from '../vanilla'
type Cleanup = () => void
type WatchGet = <T extends object>(proxyObject: T) => T
type WatchCallback = (get: WatchGet) => Cleanup | void | undefined
interface WatchOptions {
type WatchOptions = {
sync?: boolean
}

Expand Down
27 changes: 15 additions & 12 deletions src/vanilla.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@ const HANDLER = __DEV__ ? Symbol('HANDLER') : Symbol()
const PROMISE_RESULT = __DEV__ ? Symbol('PROMISE_RESULT') : Symbol()
const PROMISE_ERROR = __DEV__ ? Symbol('PROMISE_ERROR') : Symbol()

/**
* This not a public API.
* It can be changed without notice.
*/
export interface INTERNAL_AsRef {
type AsRef = {
$$valtioRef: true
}
const refSet = new WeakSet()
export function ref<T extends object>(o: T): T & INTERNAL_AsRef {
export function ref<T extends object>(o: T): T & AsRef {
refSet.add(o)
return o as T & INTERNAL_AsRef
return o as T & AsRef
}

const isObject = (x: unknown): x is object =>
Expand Down Expand Up @@ -249,17 +245,24 @@ export function subscribe<T extends object>(
}

type AnyFunction = (...args: any[]) => any
type Snapshot<T> = T extends AnyFunction

/**
* This is not a public API.
* It can be changed without any notice.
*/
export type INTERNAL_Snapshot<T> = T extends AnyFunction
? T
: T extends INTERNAL_AsRef
: T extends AsRef
? T
: T extends Promise<infer V>
? Snapshot<V>
? INTERNAL_Snapshot<V>
: {
readonly [K in keyof T]: Snapshot<T[K]>
readonly [K in keyof T]: INTERNAL_Snapshot<T[K]>
}

export function snapshot<T extends object>(proxyObject: T): Snapshot<T> {
export function snapshot<T extends object>(
proxyObject: T
): INTERNAL_Snapshot<T> {
if (__DEV__ && !(proxyObject as any)?.[SNAPSHOT]) {
console.warn('Please use proxy object')
}
Expand Down
2 changes: 1 addition & 1 deletion tests/derive.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ describe('glitch free', () => {
})

describe('two derived properties', () => {
interface State {
type State = {
a: number
derived1?: unknown
derived2?: unknown
Expand Down

1 comment on commit f09d6c8

@vercel
Copy link

@vercel vercel bot commented on f09d6c8 Jul 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

valtio – ./

valtio-pmndrs.vercel.app
valtio.vercel.app
valtio-git-main-pmndrs.vercel.app
valtio.pmnd.rs

Please sign in to comment.