diff --git a/examples/index.ts b/examples/index.ts index cd51a6972..791529c88 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -1,16 +1,141 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, Indexer, Accessor } from '@sinclair/typebox' +import { Type, Accessor, TemplateLiteralParser, TemplateLiteralFinite, TemplateLiteralResolver, TypeGuard, TIntersect, TUnion, TTemplateLiteral, IsTemplateLiteralFiniteCheck, UnionToTuple, Static, TSchema, TLiteralValue, TLiteral, TNumber, TInteger, TBigInt, TString, IndexerOptions, PatternNumber } from '@sinclair/typebox' import { TObject } from '@sinclair/typebox' -const A = Indexer.Keys(Type.Number(), { includePatterns: true }) +// function TTemplateLiteral(schema: TTemplateLiteral, options: IndexerOptions): string[] { +// const expression = TemplateLiteralParser.ParseExact(schema.pattern) +// const finite = TemplateLiteralFinite.Check(expression) +// if (!finite && options.includePatterns) return [schema.pattern] +// if (!finite && !options.includePatterns) return [] +// const resolved = TemplateLiteralResolver.Resolve(schema) +// return TypeGuard.TUnionLiteral(resolved) ? resolved.anyOf.map((schema) => schema.const.toString()) : [] +// } -const B = Accessor.Resolve( - Type.Object({ - x: Type.Number(), - }), - A, -) +// -------------------------------------------------------------------------- +// TIndexer +// -------------------------------------------------------------------------- +export type TIndexerTemplateLiteral> = F extends true ? UnionToTuple> : [] +export type TIndexerUnion = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [...TIndexer, ...TIndexerUnion] : [] +export type TIndexerLiteral = T extends PropertyKey ? [T] : [] +// prettier-ignore +export type TIndexer = + T extends TTemplateLiteral ? TIndexerTemplateLiteral : + T extends TUnion ? TIndexerUnion : + T extends TLiteral ? TIndexerLiteral : + T extends TNumber ? ['number'] : // tuple + array indexing + T extends TInteger ? ['number'] : // tuple + array indexing + [] -console.log(A, B) +export namespace Indexer { + function TTemplateLiteral(schema: TTemplateLiteral): string[] { + const expression = TemplateLiteralParser.ParseExact(schema.pattern) + const finite = TemplateLiteralFinite.Check(expression) + if (!finite) return [] + const resolved = TemplateLiteralResolver.Resolve(schema) + return TypeGuard.TUnionLiteral(resolved) ? resolved.anyOf.map((schema) => schema.const.toString()) : [] + } + function Union(schema: TSchema[]): string[] { + const [L, ...R] = schema + return (schema.length > 0) ? [...Keys(L), ...Union(R)] : [] + } + function TLiteral(schema: TLiteral): string[] { + return [schema.const.toString()] + } + function Visit(schema: TSchema) { + return ( + TypeGuard.TTemplateLiteral(schema) ? TTemplateLiteral(schema) : + TypeGuard.TUnion(schema) ? Union(schema.anyOf) : + TypeGuard.TLiteral(schema) ? TLiteral(schema) : + TypeGuard.TNumber(schema) ? ['number'] : // tuple + array indexing + TypeGuard.TInteger(schema) ? ['number'] : // tuple + array indexing + [] + ) + } + export function Pattern(schema: TSchema) { + const keys = Keys(schema) + const pattern = keys.map((key) => key === 'number' ? PatternNumber : key) + return `^(${pattern.join('|')})$` + } + export function Keys(schema: TSchema) { + return [...new Set(Visit(schema))] + } +} + +const K = Indexer.Pattern(Type.Union([ + Type.String(), + Type.Number(), + Type.String() +])) + + + +console.log(K) + +// export interface IndexerOptions { +// includePatterns: boolean +// } +// // prettier-ignore +// export namespace Indexer { +// function UnwrapPattern(key: string) { +// return key[0] === '^' && key[key.length - 1] === '$' ? key.slice(1, key.length - 1) : key +// } + +// function TIntersect(schema: TIntersect, options: IndexerOptions): string[] { +// return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[]) +// } +// function TUnion(schema: TUnion, options: IndexerOptions): string[] { +// const sets = schema.anyOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[]) +// console.log('Accessor Union', sets) +// return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set())] +// } +// function TObject(schema: TObject, options: IndexerOptions): string[] { +// return Object.getOwnPropertyNames(schema.properties) +// } +// function TRecord(schema: TRecord, options: IndexerOptions): string[] { +// return options.includePatterns ? Object.getOwnPropertyNames(schema.patternProperties) : [] +// } +// function TLiteral(schema: TLiteral, options: IndexerOptions): string[] { +// return [schema.const.toString()] +// } +// function TNumber(schema: TNumber, options: IndexerOptions): string[] { +// return ['number'] +// } +// function TInteger(schema: TInteger, options: IndexerOptions): string[] { +// return ['number'] +// } +// function Visit(schema: TSchema, options: IndexerOptions): string[] { +// return ( +// TypeGuard.TTemplateLiteral(schema) ? TTemplateLiteral(schema, options) : +// TypeGuard.TIntersect(schema) ? TIntersect(schema, options) : +// TypeGuard.TUnion(schema) ? TUnion(schema, options) : +// TypeGuard.TObject(schema) ? TObject(schema, options) : +// TypeGuard.TRecord(schema) ? TRecord(schema, options) : +// TypeGuard.TLiteral(schema) ? TLiteral(schema, options) : +// TypeGuard.TInteger(schema) ? TInteger(schema, options) : +// TypeGuard.TNumber(schema) ? TNumber(schema, options) : +// [] +// ) +// } +// /** Resolves a regular expression pattern matching all keys in this schema */ +// export function Pattern(schema: TSchema): string { +// const keys = Keys(schema, { includePatterns: true }) +// const pattern = keys.map((key) => `(${UnwrapPattern(key)})`) +// return `^(${pattern.join('|')})$` +// } +// /** Resolves an array of keys derived from this schema */ +// export function Keys(schema: TSchema, options: IndexerOptions): string[] { +// console.log('Indexer', Visit(schema, options)) +// return [...new Set(Visit(schema, options))] +// } +// } + +// const T = Type.Object({ +// 0: Type.Number(), +// 1: Type.String(), +// 2: Type.Boolean(), +// }) +// const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(2)])) + +// console.log(I) diff --git a/src/typebox.ts b/src/typebox.ts index 04a8f048c..9fadd4219 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -3058,23 +3058,6 @@ export class JsonTypeBuilder extends TypeBuilder { this.Never(options) ) as TExtract } - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index(schema: T, keys: K, options?: SchemaOptions): AssertType - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index(schema: T, keys: K, options?: SchemaOptions): UnionType> - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index(schema: T, keys: K, options?: SchemaOptions): TIndex> - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index>(schema: T, keys: K, options?: SchemaOptions): TIndex - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> - // /** `[Json]` Returns an Indexed property type for the given keys */ - // public Index(schema: T, key: K, options?: SchemaOptions): TSchema - /** `[Json]` Returns an Indexed property type for the given keys */ public Index>(schema: T, keys: I, options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */