From aefb769855315c796576cea97769cb676a6b64f9 Mon Sep 17 00:00:00 2001 From: Aria Minaei Date: Mon, 6 Sep 2021 11:05:35 +0200 Subject: [PATCH] More progress on shorthand props --- theatre/core/src/propTypes/index.ts | 84 ++++-------------------- theatre/core/src/propTypes/internals.ts | 85 ++++++++++++++++++++++++- theatre/shared/src/testUtils.ts | 2 +- 3 files changed, 97 insertions(+), 74 deletions(-) diff --git a/theatre/core/src/propTypes/index.ts b/theatre/core/src/propTypes/index.ts index 6bba38e..24ad99f 100644 --- a/theatre/core/src/propTypes/index.ts +++ b/theatre/core/src/propTypes/index.ts @@ -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 userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue' -import {isLonghandPropType, propTypeSymbol, toLonghandProp} from './internals' +import type { + IShorthandCompoundProps, + IValidCompoundProps, + ShorthandCompoundPropsToLonghandCompoundProps, +} from './internals' +import {sanitizeCompoundProps} from './internals' +import {propTypeSymbol} from './internals' /** * Creates a compound prop type (basically a JS object). @@ -38,57 +27,16 @@ import {isLonghandPropType, propTypeSymbol, toLonghandProp} from './internals' * @param extras * @returns * - * @category Prop type definitions */ -export const compound = ( +export const compound = ( props: Props, extras?: PropTypeConfigExtras, -): PropTypeConfig_Compound => { - let sanitizedProps: 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) { - 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} - } - +): PropTypeConfig_Compound< + ShorthandCompoundPropsToLonghandCompoundProps +> => { return { type: 'compound', - props: sanitizedProps, + props: sanitizeCompoundProps(props), valueType: null as $IntentionalAny, [propTypeSymbol]: 'TheatrePropType', label: extras?.label, @@ -101,7 +49,6 @@ export const compound = ( * @param opts * @returns * - * @category Prop type definitions */ export const number = ( defaultValue: number, @@ -130,7 +77,6 @@ export const number = ( * @param extras * @returns * - * @category Prop type definitions */ export const boolean = ( defaultValue: boolean, @@ -151,7 +97,6 @@ export const boolean = ( * @param extras * @returns * - * @category Prop type definitions */ export const string = ( defaultValue: string, @@ -173,7 +118,6 @@ export const string = ( * @param extras * @returns * - * @category Prop type definitions */ export function stringLiteral( defaultValue: Extract, @@ -262,10 +206,6 @@ export interface PropTypeConfig_StringLiteral as: 'menu' | 'switch' } -type IValidCompoundProps = { - [K in string]: PropTypeConfig -} - /** * @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 diff --git a/theatre/core/src/propTypes/internals.ts b/theatre/core/src/propTypes/internals.ts index b1daf21..5f2fa22 100644 --- a/theatre/core/src/propTypes/internals.ts +++ b/theatre/core/src/propTypes/internals.ts @@ -2,11 +2,50 @@ import {InvalidArgumentError} from '@theatre/shared/utils/errors' import type {$IntentionalAny} from '@theatre/shared/utils/types' import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue' 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' 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 string + ? PropTypeConfig_String + : P extends number + ? PropTypeConfig_Number + : P extends boolean + ? PropTypeConfig_Boolean + : P extends PropTypeConfig + ? P + : P extends IShorthandCompoundProps + ? PropTypeConfig_Compound> + : never + +export type ShorthandCompoundPropsToLonghandCompoundProps< + P extends IShorthandCompoundProps, +> = { + [K in keyof P]: ShorthandPropToLonghandProp +} + export function isLonghandPropType(t: unknown): t is PropTypeConfig { return ( 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 +} diff --git a/theatre/shared/src/testUtils.ts b/theatre/shared/src/testUtils.ts index f32a8cf..b67e0c1 100644 --- a/theatre/shared/src/testUtils.ts +++ b/theatre/shared/src/testUtils.ts @@ -35,7 +35,7 @@ export async function setupTestSheet(sheetState: SheetState_Historic) { 'obj', t.compound({ position: { - x: t.number(0), + x: 0, y: t.number(1), z: t.number(2), },