refactor: Add working Nominal types, clarify identifiers
* Use more Nominal types to help with internal code id usage consistency * Broke apart StudioHistoricState type Co-authored-by: Aria <aria.minaei@gmail.com>
This commit is contained in:
parent
9d9fc1680e
commit
1387ce62d2
58 changed files with 647 additions and 299 deletions
|
@ -25,6 +25,8 @@ enum ValueTypes {
|
|||
export interface IdentityDerivationProvider {
|
||||
/**
|
||||
* @internal
|
||||
* Future: We could consider using a `Symbol.for("dataverse/IdentityDerivationProvider")` as a key here, similar to
|
||||
* how {@link Iterable} works for `of`.
|
||||
*/
|
||||
readonly $$isIdentityDerivationProvider: true
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,11 @@ const cachedSubPathPointersWeakMap = new WeakMap<
|
|||
* A wrapper type for the type a `Pointer` points to.
|
||||
*/
|
||||
export type PointerType<O> = {
|
||||
/**
|
||||
* Only accessible via the type system.
|
||||
* This is a helper for getting the underlying pointer type
|
||||
* via the type space.
|
||||
*/
|
||||
$$__pointer_type: O
|
||||
}
|
||||
|
||||
|
@ -53,21 +58,28 @@ export type PointerType<O> = {
|
|||
*
|
||||
* The current solution is to just avoid using `any` with pointer-related code (or type-test it well).
|
||||
* But if you enjoy solving typescript puzzles, consider fixing this :)
|
||||
*
|
||||
* Potentially, [TypeScript variance annotations in 4.7+](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#optional-variance-annotations-for-type-parameters)
|
||||
* might be able to help us.
|
||||
*/
|
||||
export type Pointer<O> = PointerType<O> &
|
||||
(O extends UnindexableTypesForPointer
|
||||
? UnindexablePointer
|
||||
: unknown extends O
|
||||
? UnindexablePointer
|
||||
: O extends (infer T)[]
|
||||
? Pointer<T>[]
|
||||
: O extends {}
|
||||
? {
|
||||
[K in keyof O]-?: Pointer<O[K]>
|
||||
} /*&
|
||||
{[K in string | number]: Pointer<K extends keyof O ? O[K] : undefined>}*/
|
||||
: UnindexablePointer)
|
||||
// `Exclude<O, undefined>` will remove `undefined` from the first type
|
||||
// `undefined extends O ? undefined : never` will give us `undefined` if `O` is `... | undefined`
|
||||
PointerInner<Exclude<O, undefined>, undefined extends O ? undefined : never>
|
||||
|
||||
// By separating the `O` (non-undefined) from the `undefined` or `never`, we
|
||||
// can properly use `O extends ...` to determine the kind of potential value
|
||||
// without actually discarding optionality information.
|
||||
type PointerInner<O, Optional> = O extends UnindexableTypesForPointer
|
||||
? UnindexablePointer
|
||||
: unknown extends O
|
||||
? UnindexablePointer
|
||||
: O extends (infer T)[]
|
||||
? Pointer<T>[]
|
||||
: O extends {}
|
||||
? {
|
||||
[K in keyof O]-?: Pointer<O[K] | Optional>
|
||||
}
|
||||
: UnindexablePointer
|
||||
|
||||
const pointerMetaSymbol = Symbol('pointerMeta')
|
||||
|
||||
|
|
106
packages/dataverse/src/pointer.typeTest.ts
Normal file
106
packages/dataverse/src/pointer.typeTest.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
import type {Pointer, UnindexablePointer} from './pointer'
|
||||
import type {$IntentionalAny} from './types'
|
||||
|
||||
const nominal = Symbol()
|
||||
type Nominal<Name> = string & {[nominal]: Name}
|
||||
|
||||
type Key = Nominal<'key'>
|
||||
type Id = Nominal<'id'>
|
||||
|
||||
type IdObject = {
|
||||
inner: true
|
||||
}
|
||||
|
||||
type KeyObject = {
|
||||
inner: {
|
||||
byIds: Partial<Record<Id, IdObject>>
|
||||
}
|
||||
}
|
||||
|
||||
type NestedNominalThing = {
|
||||
optional?: true
|
||||
byKeys: Partial<Record<Key, KeyObject>>
|
||||
}
|
||||
|
||||
interface TypeError<M> {}
|
||||
|
||||
type Debug<T extends 0> = T
|
||||
type IsTrue<T extends true> = T
|
||||
type IsFalse<F extends false> = F
|
||||
type IsExtends<F, R extends F> = F
|
||||
type IsExactly<F, R extends F> = F extends R
|
||||
? true
|
||||
: TypeError<[F, 'does not extend', R]>
|
||||
|
||||
function test() {
|
||||
const p = todo<Pointer<NestedNominalThing>>()
|
||||
const key1 = todo<Key>()
|
||||
const id1 = todo<Id>()
|
||||
|
||||
type A = UnindexablePointer[typeof key1]
|
||||
type BaseChecks = [
|
||||
IsExtends<any, any>,
|
||||
IsExtends<undefined | 1, undefined>,
|
||||
IsExtends<string, Key>,
|
||||
IsTrue<IsExactly<UnindexablePointer[typeof key1], Pointer<undefined>>>,
|
||||
IsTrue<
|
||||
IsExactly<Pointer<undefined | true>['...']['...'], Pointer<undefined>>
|
||||
>,
|
||||
IsTrue<
|
||||
IsExactly<
|
||||
Pointer<Record<Key, true | undefined>>[Key],
|
||||
Pointer<true | undefined>
|
||||
>
|
||||
>,
|
||||
IsTrue<IsExactly<Pointer<undefined>[Key], Pointer<undefined>>>,
|
||||
// Debug<Pointer<undefined | Record<string, true>>[Key]>,
|
||||
IsTrue<IsExactly<Pointer<Record<string, true>>[string], Pointer<true>>>,
|
||||
IsTrue<
|
||||
IsExactly<
|
||||
Pointer<undefined | Record<string, true>>[string],
|
||||
Pointer<true | undefined>
|
||||
>
|
||||
>,
|
||||
IsTrue<
|
||||
IsExactly<
|
||||
Pointer<undefined | Record<Key, true>>[Key],
|
||||
Pointer<true | undefined>
|
||||
>
|
||||
>,
|
||||
// Debug<Pointer<undefined | true>['...']['...']>,
|
||||
// IsFalse<any extends Pointer<undefined | true> ? true : false>,
|
||||
// what extends what
|
||||
IsTrue<1 & undefined extends undefined ? true : false>,
|
||||
IsFalse<1 | undefined extends undefined ? true : false>,
|
||||
]
|
||||
|
||||
t<Pointer<undefined | true>>() //
|
||||
.isExactly(p.optional).ok
|
||||
|
||||
t<Pointer<undefined | KeyObject>>() //
|
||||
.isExactly(p.byKeys[key1]).ok
|
||||
|
||||
t<Pointer<undefined | KeyObject['inner']>>() //
|
||||
.isExactly(p.byKeys[key1].inner).ok
|
||||
|
||||
t<Pointer<undefined | IdObject>>() //
|
||||
.isExactly(p.byKeys[key1].inner.byIds[id1]).ok
|
||||
|
||||
p.byKeys[key1]
|
||||
}
|
||||
|
||||
function todo<T>(hmm?: TemplateStringsArray): T {
|
||||
return null as $IntentionalAny
|
||||
}
|
||||
function t<T>(): {
|
||||
isExactly<R extends T>(
|
||||
hmm: R,
|
||||
): T extends R
|
||||
? // any extends R
|
||||
// ? TypeError<[R, 'is any']>
|
||||
// :
|
||||
{ok: true}
|
||||
: TypeError<[T, 'does not extend', R]>
|
||||
} {
|
||||
return {isExactly: (hmm) => hmm as $IntentionalAny}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue