@reallygoodwork/coral-core
Types & Guards
Type definitions and type guard functions.
Types & Guards
Type definitions and utility functions for working with Coral specifications.
Type Safety
Coral Core is fully type-safe with zero any types and no type assertions:
- All
anytypes removed - Replaced withunknownand proper type guards - No
asassertions - Type guards provide safe type narrowing - Recursive types -
CoralStyleTypeis an interface supporting nested styles - Nullable types -
CoralTSTypesisz.nullable- returnsnullwhen type cannot be determined
Type Guards
Type guards provide safe type narrowing without type assertions:
import {
// Reference type guards
isTokenReference,
isPropReference,
isComponentReference,
isAssetReference,
isExternalReference,
isAnyReference,
// Conditional type guards
isConditionalExpression,
// Binding type guards
isPropTransform,
isEventReference,
isComputedValue,
isInlineHandler,
// Composition type guards
isComponentInstance,
isPropBinding,
isSlotForward,
} from '@reallygoodwork/coral-core'
// Example usage - TypeScript automatically narrows types
if (isTokenReference(value)) {
// value.$token is available - TypeScript knows this is TokenReference
console.log(`Token path: ${value.$token}`)
}
if (isComponentInstance(node)) {
// node.$component is available - TypeScript knows this is ComponentInstance
console.log(`Component ref: ${node.$component.ref}`)
}Extended CoralNode Fields
type CoralNode = {
// ... existing fields ...
// Variant styles
variantStyles?: NodeVariantStyles
compoundVariantStyles?: CompoundVariantStyle[]
stateStyles?: StateStyles
// Conditional rendering
conditional?: ConditionalExpression
conditionalBehavior?: 'hide' | 'dim' | 'outline'
conditionalStyles?: ConditionalStyle[]
// Slots
slotTarget?: string
slotFallback?: CoralNode[]
// Component instances (when type === 'COMPONENT_INSTANCE')
$component?: ComponentInstanceRef
propBindings?: Record<string, PropBinding>
eventBindings?: Record<string, EventBinding>
slotBindings?: Record<string, SlotBinding>
variantOverrides?: Record<string, string>
styleOverrides?: CoralStyleType
// Element bindings
eventHandlers?: Record<string, EventBinding>
ariaAttributes?: Record<string, BindableValue>
dataAttributes?: Record<string, BindableValue>
}Extended CoralRootNode Fields
type CoralRootNode = CoralNode & {
// ... existing fields ...
// Enhanced metadata
$meta?: ComponentMeta
// Component-level variants
componentVariants?: ComponentVariants
// Typed props (extends componentProperties)
props?: ComponentPropsDefinition
// Typed events (extends methods)
events?: ComponentEventsDefinition
// Slot definitions
slots?: SlotDefinition[]
}Reference Types
type TokenReference = {
$token: string
$fallback?: unknown
}
type PropReference = {
$prop: string
}
type ComponentReference = {
$component: {
ref: string
version?: string
}
}
type AssetReference = {
$asset: string
$fallback?: string
}
type ExternalReference = {
$external: string
}Variant Types
type VariantAxis = {
name: string
values: string[]
default: string
description?: string
}
type ComponentVariants = {
axes: VariantAxis[]
compounds?: CompoundVariantCondition[]
}
type CompoundVariantCondition = {
conditions: Record<string, string>
description?: string
}
type NodeVariantStyles = Record<string, Record<string, CoralStyleType>>
type CompoundVariantStyle = {
conditions: Record<string, string>
styles: CoralStyleType
}
type StateStyles = Record<string, CoralStyleType | NodeVariantStyles>Prop Types
type PrimitivePropType = 'string' | 'number' | 'boolean' | 'ReactNode'
type PropType =
| PrimitivePropType
| { enum: string[] }
| { array: PropType }
| { object: Record<string, PropType> }
| { union: PropType[] }
type ComponentPropDefinition = {
type: PropType
default?: unknown
required?: boolean
description?: string
editorControl?: EditorControl
constraints?: PropConstraints
}
type ComponentPropsDefinition = Record<string, ComponentPropDefinition>Event Types
type EventParameter = {
name: string
type: string
optional?: boolean
}
type ComponentEventDefinition = {
description?: string
parameters?: EventParameter[]
deprecated?: boolean | string
}
type ComponentEventsDefinition = Record<string, ComponentEventDefinition>Conditional Types
type ConditionalExpression =
| { $prop: string }
| { $not: ConditionalExpression }
| { $and: ConditionalExpression[] }
| { $or: ConditionalExpression[] }
| { $eq: [ConditionalExpression, unknown] }
| { $ne: [ConditionalExpression, unknown] }
type ConditionalBehavior = 'hide' | 'dim' | 'outline'
type ConditionalStyle = {
condition: ConditionalExpression
styles: CoralStyleType
}Slot Types
type SlotDefinition = {
name: string
description?: string
required?: boolean
multiple?: boolean
allowedElements?: string[]
allowedComponents?: string[]
defaultContent?: CoralNode[]
}
type SlotBinding =
| CoralNode
| CoralNode[]
| { $prop: string }
| { $slot: string }Binding Types
type PropTransform = {
$prop: string
$transform: 'boolean' | 'not' | 'string' | 'number'
}
type ComputedValue = {
$computed: 'concat' | 'ternary'
$inputs: unknown[]
}
type EventReference = {
$event: string
$params?: unknown[]
}
type InlineHandler = {
$inline: string
}
type EventBinding = EventReference | InlineHandler
type BindableValue =
| string
| number
| boolean
| { $prop: string }
| PropTransform
| ComputedValue
| { $token: string }Package Types
type CoralConfig = {
$schema?: string
name: string
version: string
description?: string
coral: CoralSpec
tokens?: TokensConfig
components?: ComponentsConfig
assets?: AssetsConfig
presets?: PresetsConfig
extends?: string[]
exports?: Record<string, ExportTarget>
}
type LoadedPackage = {
config: CoralConfig
configPath: string
components: Map<string, CoralRootNode>
tokens: Map<string, Record<string, unknown>>
componentIndex?: ComponentIndex
tokenIndex?: TokenIndex
}Core Type Definitions
CoralTSTypes
// CoralTSTypes is nullable - can be null when type cannot be determined
type CoralTSTypes =
| 'string'
| 'number'
| 'boolean'
| 'array'
| 'object'
| 'null'
| 'undefined'
| 'function'
| null // Returned when type cannot be determined
// Defined as z.nullable in the schema
export const zCoralTSTypes = z.nullable(
z.union([
z.literal('string'),
z.literal('number'),
z.literal('boolean'),
z.literal('array'),
z.literal('object'),
z.literal('null'),
z.literal('undefined'),
z.literal('function'),
])
)CoralStyleType
// CoralStyleType is an interface (not type alias) to support recursive references
interface CoralStyleType {
[key: string]:
| string
| number
| CoralColorType
| CoralGradientType
| Dimension
| CoralStyleType // Recursive for nested styles (e.g., responsive breakpoints)
}
// Schema uses z.lazy() for recursive validation
export const zCoralStyleSchema: z.ZodType<CoralStyleType> = z.lazy(() =>
z.record(
z.string(),
z.union([
zCoralStyleValueSchema,
z.record(z.string(), z.union([zCoralStyleValueSchema, zCoralStyleSchema]))
])
)
)Type Safety Best Practices
-
Use type guards instead of assertions:
// ❌ Bad - type assertion const value = node.textContent as PropReference // ✅ Good - type guard if (isPropReference(node.textContent)) { // TypeScript knows node.textContent is PropReference here console.log(node.textContent.$prop) } -
Handle nullable types properly:
// CoralTSTypes can be null const propType: CoralTSTypes = getTypeFromAnnotation(annotation) if (propType === null) { // Handle unknown type } else { // propType is one of the literal types } -
Use
unknownwith type guards:// ❌ Bad - any bypasses type checking function process(value: any) { ... } // ✅ Good - unknown requires type narrowing function process(value: unknown) { if (isTokenReference(value)) { // TypeScript knows value is TokenReference } }
Related
- Variants - Variant system
- Props & Events - Typed APIs
- References - Reference types