More progress on shorthand props
This commit is contained in:
parent
2f44f53021
commit
aefb769855
3 changed files with 97 additions and 74 deletions
|
@ -1,22 +1,11 @@
|
||||||
/**
|
|
||||||
* Usage:
|
|
||||||
* ```ts
|
|
||||||
* import {types as t} from '@theatre/core'
|
|
||||||
*
|
|
||||||
* const props = t.compound({
|
|
||||||
* x: t.number(0),
|
|
||||||
* y: t.number(0)
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* const obj = sheet.obj("An object", props)
|
|
||||||
* ```
|
|
||||||
* @module types
|
|
||||||
*/
|
|
||||||
import {InvalidArgumentError} from '@theatre/shared/utils/errors'
|
|
||||||
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue'
|
import type {
|
||||||
import {isLonghandPropType, propTypeSymbol, toLonghandProp} from './internals'
|
IShorthandCompoundProps,
|
||||||
|
IValidCompoundProps,
|
||||||
|
ShorthandCompoundPropsToLonghandCompoundProps,
|
||||||
|
} from './internals'
|
||||||
|
import {sanitizeCompoundProps} from './internals'
|
||||||
|
import {propTypeSymbol} from './internals'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a compound prop type (basically a JS object).
|
* Creates a compound prop type (basically a JS object).
|
||||||
|
@ -38,57 +27,16 @@ import {isLonghandPropType, propTypeSymbol, toLonghandProp} from './internals'
|
||||||
* @param extras
|
* @param extras
|
||||||
* @returns
|
* @returns
|
||||||
*
|
*
|
||||||
* @category Prop type definitions
|
|
||||||
*/
|
*/
|
||||||
export const compound = <Props extends IValidCompoundProps>(
|
export const compound = <Props extends IShorthandCompoundProps>(
|
||||||
props: Props,
|
props: Props,
|
||||||
extras?: PropTypeConfigExtras,
|
extras?: PropTypeConfigExtras,
|
||||||
): PropTypeConfig_Compound<Props> => {
|
): PropTypeConfig_Compound<
|
||||||
let sanitizedProps: Props
|
ShorthandCompoundPropsToLonghandCompoundProps<Props>
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
> => {
|
||||||
sanitizedProps = {} as $IntentionalAny
|
|
||||||
if (typeof props !== 'object' || !props) {
|
|
||||||
throw new InvalidArgumentError(
|
|
||||||
`t.compound() expects an object, like: {x: 10}. ${userReadableTypeOfValue(
|
|
||||||
props,
|
|
||||||
)} given.`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for (const key of Object.keys(props) as Array<keyof Props>) {
|
|
||||||
if (typeof key !== 'string') {
|
|
||||||
throw new InvalidArgumentError(
|
|
||||||
`t.compound()'s keys must be all strings. ${userReadableTypeOfValue(
|
|
||||||
key,
|
|
||||||
)} given.`,
|
|
||||||
)
|
|
||||||
} else if (key.length === 0 || !key.match(/^\w+$/)) {
|
|
||||||
throw new InvalidArgumentError(
|
|
||||||
`compound key ${userReadableTypeOfValue(
|
|
||||||
key,
|
|
||||||
)} is invalid. The keys must be alphanumeric and start with a letter.`,
|
|
||||||
)
|
|
||||||
} else if (key.length > 64) {
|
|
||||||
throw new InvalidArgumentError(
|
|
||||||
`compound key ${userReadableTypeOfValue(key)} is too long.`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const val = props[key]
|
|
||||||
if (isLonghandPropType(val)) {
|
|
||||||
sanitizedProps[key as keyof Props] = val as $IntentionalAny
|
|
||||||
} else {
|
|
||||||
sanitizedProps[key as keyof Props] = toLonghandProp(
|
|
||||||
val,
|
|
||||||
) as $IntentionalAny
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sanitizedProps = {...props}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'compound',
|
type: 'compound',
|
||||||
props: sanitizedProps,
|
props: sanitizeCompoundProps(props),
|
||||||
valueType: null as $IntentionalAny,
|
valueType: null as $IntentionalAny,
|
||||||
[propTypeSymbol]: 'TheatrePropType',
|
[propTypeSymbol]: 'TheatrePropType',
|
||||||
label: extras?.label,
|
label: extras?.label,
|
||||||
|
@ -101,7 +49,6 @@ export const compound = <Props extends IValidCompoundProps>(
|
||||||
* @param opts
|
* @param opts
|
||||||
* @returns
|
* @returns
|
||||||
*
|
*
|
||||||
* @category Prop type definitions
|
|
||||||
*/
|
*/
|
||||||
export const number = (
|
export const number = (
|
||||||
defaultValue: number,
|
defaultValue: number,
|
||||||
|
@ -130,7 +77,6 @@ export const number = (
|
||||||
* @param extras
|
* @param extras
|
||||||
* @returns
|
* @returns
|
||||||
*
|
*
|
||||||
* @category Prop type definitions
|
|
||||||
*/
|
*/
|
||||||
export const boolean = (
|
export const boolean = (
|
||||||
defaultValue: boolean,
|
defaultValue: boolean,
|
||||||
|
@ -151,7 +97,6 @@ export const boolean = (
|
||||||
* @param extras
|
* @param extras
|
||||||
* @returns
|
* @returns
|
||||||
*
|
*
|
||||||
* @category Prop type definitions
|
|
||||||
*/
|
*/
|
||||||
export const string = (
|
export const string = (
|
||||||
defaultValue: string,
|
defaultValue: string,
|
||||||
|
@ -173,7 +118,6 @@ export const string = (
|
||||||
* @param extras
|
* @param extras
|
||||||
* @returns
|
* @returns
|
||||||
*
|
*
|
||||||
* @category Prop type definitions
|
|
||||||
*/
|
*/
|
||||||
export function stringLiteral<Opts extends {[key in string]: string}>(
|
export function stringLiteral<Opts extends {[key in string]: string}>(
|
||||||
defaultValue: Extract<keyof Opts, string>,
|
defaultValue: Extract<keyof Opts, string>,
|
||||||
|
@ -262,10 +206,6 @@ export interface PropTypeConfig_StringLiteral<T extends string>
|
||||||
as: 'menu' | 'switch'
|
as: 'menu' | 'switch'
|
||||||
}
|
}
|
||||||
|
|
||||||
type IValidCompoundProps = {
|
|
||||||
[K in string]: PropTypeConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Determine if 'compound' is a clear term for what this is.
|
* @todo Determine if 'compound' is a clear term for what this is.
|
||||||
* I didn't want to use 'object' as it could get confused with
|
* I didn't want to use 'object' as it could get confused with
|
||||||
|
|
|
@ -2,11 +2,50 @@ import {InvalidArgumentError} from '@theatre/shared/utils/errors'
|
||||||
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue'
|
import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue'
|
||||||
import {isPlainObject} from 'lodash-es'
|
import {isPlainObject} from 'lodash-es'
|
||||||
import type {PropTypeConfig} from './index'
|
import type {
|
||||||
|
PropTypeConfig,
|
||||||
|
PropTypeConfig_Boolean,
|
||||||
|
PropTypeConfig_Compound,
|
||||||
|
PropTypeConfig_Number,
|
||||||
|
PropTypeConfig_String,
|
||||||
|
} from './index'
|
||||||
import * as t from './index'
|
import * as t from './index'
|
||||||
|
|
||||||
export const propTypeSymbol = Symbol('TheatrePropType_Basic')
|
export const propTypeSymbol = Symbol('TheatrePropType_Basic')
|
||||||
|
|
||||||
|
export type IValidCompoundProps = {
|
||||||
|
[K in string]: PropTypeConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type IShorthandProp =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| PropTypeConfig
|
||||||
|
| IShorthandCompoundProps
|
||||||
|
|
||||||
|
export type IShorthandCompoundProps = {
|
||||||
|
[K in string]: IShorthandProp
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShorthandPropToLonghandProp<P extends IShorthandProp> = P extends string
|
||||||
|
? PropTypeConfig_String
|
||||||
|
: P extends number
|
||||||
|
? PropTypeConfig_Number
|
||||||
|
: P extends boolean
|
||||||
|
? PropTypeConfig_Boolean
|
||||||
|
: P extends PropTypeConfig
|
||||||
|
? P
|
||||||
|
: P extends IShorthandCompoundProps
|
||||||
|
? PropTypeConfig_Compound<ShorthandCompoundPropsToLonghandCompoundProps<P>>
|
||||||
|
: never
|
||||||
|
|
||||||
|
export type ShorthandCompoundPropsToLonghandCompoundProps<
|
||||||
|
P extends IShorthandCompoundProps,
|
||||||
|
> = {
|
||||||
|
[K in keyof P]: ShorthandPropToLonghandProp<P[K]>
|
||||||
|
}
|
||||||
|
|
||||||
export function isLonghandPropType(t: unknown): t is PropTypeConfig {
|
export function isLonghandPropType(t: unknown): t is PropTypeConfig {
|
||||||
return (
|
return (
|
||||||
typeof t === 'object' &&
|
typeof t === 'object' &&
|
||||||
|
@ -37,3 +76,47 @@ export function toLonghandProp(p: unknown): PropTypeConfig {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sanitizeCompoundProps(
|
||||||
|
props: IShorthandCompoundProps,
|
||||||
|
): IValidCompoundProps {
|
||||||
|
const sanitizedProps: IValidCompoundProps = {}
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
if (typeof props !== 'object' || !props) {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`t.compound() expects an object, like: {x: 10}. ${userReadableTypeOfValue(
|
||||||
|
props,
|
||||||
|
)} given.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const key of Object.keys(props)) {
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`t.compound()'s keys must be all strings. ${userReadableTypeOfValue(
|
||||||
|
key,
|
||||||
|
)} given.`,
|
||||||
|
)
|
||||||
|
} else if (key.length === 0 || !key.match(/^\w+$/)) {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`compound key ${userReadableTypeOfValue(
|
||||||
|
key,
|
||||||
|
)} is invalid. The keys must be alphanumeric and start with a letter.`,
|
||||||
|
)
|
||||||
|
} else if (key.length > 64) {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`compound key ${userReadableTypeOfValue(key)} is too long.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const val = props[key]
|
||||||
|
if (isLonghandPropType(val)) {
|
||||||
|
sanitizedProps[key] = val as $IntentionalAny
|
||||||
|
} else {
|
||||||
|
sanitizedProps[key] = toLonghandProp(val) as $IntentionalAny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sanitizedProps
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ export async function setupTestSheet(sheetState: SheetState_Historic) {
|
||||||
'obj',
|
'obj',
|
||||||
t.compound({
|
t.compound({
|
||||||
position: {
|
position: {
|
||||||
x: t.number(0),
|
x: 0,
|
||||||
y: t.number(1),
|
y: t.number(1),
|
||||||
z: t.number(2),
|
z: t.number(2),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue