Skip to content

Commit f4e42f4

Browse files
committed
refactor: use a SpecialShape to describe union and intersection type
microsoft/TypeScript#35562
1 parent 4e4a9db commit f4e42f4

File tree

2 files changed

+79
-24
lines changed

2 files changed

+79
-24
lines changed

src/index.spec.ts

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -404,39 +404,65 @@ describe('intersect', () => {
404404

405405
const case1 = t.intersect([1, Number])
406406
expectTypeOf(case1).toEqualTypeOf<t.Schema<
407-
t.Schema<1, 1> & t.Schema<NumberConstructor, number>,
407+
t.SpecialShape<
408+
t.SpecialShapes['intersection'], [
409+
t.Schema<1, 1>,
410+
t.Schema<NumberConstructor, number>
411+
]
412+
>,
408413
1
409414
>>()
410415
expectTypeOf<t.Infer<typeof case1>>()
411416
.toEqualTypeOf<1>()
412417

413418
const case2 = t.intersect([1, Number, String])
414419
expectTypeOf(case2).toEqualTypeOf<t.Schema<
415-
t.Schema<1, 1> & t.Schema<NumberConstructor, number> & t.Schema<StringConstructor, string>,
420+
t.SpecialShape<
421+
t.SpecialShapes['intersection'], [
422+
t.Schema<1, 1>,
423+
t.Schema<NumberConstructor, number>,
424+
t.Schema<StringConstructor, string>
425+
]
426+
>,
416427
never
417428
>>()
418429
expectTypeOf<t.Infer<typeof case2>>()
419430
.toEqualTypeOf<never>()
420431

421432
const case3 = t.intersect([Number, t.unknown()])
422433
expectTypeOf(case3).toEqualTypeOf<t.Schema<
423-
t.Schema<NumberConstructor, number> & t.Schema<typeof t.Symbols.unknown, unknown>,
434+
t.SpecialShape<
435+
t.SpecialShapes['intersection'], [
436+
t.Schema<NumberConstructor, number>,
437+
t.Schema<typeof t.Symbols.unknown, unknown>
438+
]
439+
>,
424440
number
425441
>>()
426442
expectTypeOf<t.Infer<typeof case3>>()
427443
.toEqualTypeOf<number>()
428444

429445
const case4 = t.intersect([Number, t.never()])
430446
expectTypeOf(case4).toEqualTypeOf<t.Schema<
431-
t.Schema<NumberConstructor, number> & t.Schema<typeof t.Symbols.never, never>,
447+
t.SpecialShape<
448+
t.SpecialShapes['intersection'], [
449+
t.Schema<NumberConstructor, number>,
450+
t.Schema<typeof t.Symbols.never, never>
451+
]
452+
>,
432453
never
433454
>>()
434455
expectTypeOf<t.Infer<typeof case4>>()
435456
.toEqualTypeOf<never>()
436457

437458
const case5 = t.intersect([Number, t.any()])
438459
expectTypeOf(case5).toEqualTypeOf<t.Schema<
439-
t.Schema<NumberConstructor, number> & t.Schema<any, any>,
460+
t.SpecialShape<
461+
t.SpecialShapes['intersection'], [
462+
t.Schema<NumberConstructor, number>,
463+
t.Schema<any, any>
464+
]
465+
>,
440466
any
441467
>>()
442468
expectTypeOf<t.Infer<typeof case5>>()
@@ -448,14 +474,24 @@ describe('intersect', () => {
448474
// because {} & null and {} & undefined just get tossed away.
449475
const case6 = t.intersect([Number, {}])
450476
expectTypeOf(case6).toEqualTypeOf<t.Schema<
451-
t.Schema<NumberConstructor, number> & t.Schema<{}, {}>,
477+
t.SpecialShape<
478+
t.SpecialShapes['intersection'], [
479+
t.Schema<NumberConstructor, number>,
480+
t.Schema<{}, {}>
481+
]
482+
>,
452483
number
453484
>>()
454485
expectTypeOf<t.Infer<typeof case6>>()
455486
.toEqualTypeOf<number>()
456487
const case7 = t.intersect([Number, t({})])
457488
expectTypeOf(case7).toEqualTypeOf<t.Schema<
458-
t.Schema<NumberConstructor, number> & t.Schema<{}, {}>,
489+
t.SpecialShape<
490+
t.SpecialShapes['intersection'], [
491+
t.Schema<NumberConstructor, number>,
492+
t.Schema<{}, {}>
493+
]
494+
>,
459495
number
460496
>>()
461497
expectTypeOf<t.Infer<typeof case7>>()
@@ -465,12 +501,12 @@ describe('intersect', () => {
465501
const case0 = t.union([1, 2, '3']).and(String)
466502
// ^?
467503
expectTypeOf(case0).toEqualTypeOf<t.Schema<
468-
& (
469-
| t.Schema<1, 1>
470-
| t.Schema<2, 2>
471-
| t.Schema<'3', '3'>
472-
)
473-
& t.Schema<StringConstructor, string>,
504+
t.SpecialShape<
505+
t.SpecialShapes['intersection'], [
506+
t.Schema<1, 1> | t.Schema<2, 2> | t.Schema<'3', '3'>,
507+
t.Schema<StringConstructor, string>
508+
]
509+
>,
474510
'3'
475511
>>()
476512
expectTypeOf<t.Infer<typeof case0>>()
@@ -517,11 +553,13 @@ describe('intersect', () => {
517553
// `a${String}`
518554
])
519555
expectTypeOf(case0).toEqualTypeOf<t.Schema<
520-
t.Schema<
521-
t.Schema<'a', 'a'> | t.Schema<'ab', 'ab'> | t.Schema<'b', 'b'>,
522-
'a' | 'ab' | 'b'
523-
> & t.Schema<`a${string}`, `a${string}`>,
524-
('a' | 'ab' | 'b') & `a${string}`
556+
t.SpecialShape<
557+
t.SpecialShapes['intersection'], [
558+
t.Schema<t.Schema<'a', 'a'> | t.Schema<'ab', 'ab'> | t.Schema<'b', 'b'>, 'a' | 'ab' | 'b'>,
559+
t.Schema<`a${string}`, `a${string}`>
560+
]
561+
>,
562+
'a' | 'ab'
525563
>>()
526564
expectTypeOf<t.Infer<typeof case0>>().toEqualTypeOf<T0>()
527565

@@ -532,11 +570,13 @@ describe('intersect', () => {
532570
`a${0 as number}`
533571
])
534572
expectTypeOf(case1).toEqualTypeOf<t.Schema<
535-
t.Schema<
536-
t.Schema<'a', 'a'> | t.Schema<'ax', 'ax'> | t.Schema<'a12', 'a12'> | t.Schema<'b', 'b'>,
537-
'a' | 'ax' | 'a12' | 'b'
538-
> & t.Schema<`a${number}`, `a${number}`>,
539-
('a' | 'ax' | 'a12' | 'b') & `a${number}`
573+
t.SpecialShape<
574+
t.SpecialShapes['intersection'], [
575+
t.Schema<t.Schema<'a', 'a'> | t.Schema<'ax', 'ax'> | t.Schema<'a12', 'a12'> | t.Schema<'b', 'b'>, 'a' | 'ax' | 'a12' | 'b'>,
576+
t.Schema<`a${number}`, `a${number}`>
577+
]
578+
>,
579+
'a12'
540580
>>()
541581
expectTypeOf<t.Infer<typeof case1>>().toEqualTypeOf<T1>()
542582
})

src/index.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ literal.Undefined = `__DO_NOT_USE_SAME_LITERAL_${
4949
}__IF_YOU_WANT_TO_USE_IT__` as const
5050

5151
export namespace t {
52+
export const specialShapes = {
53+
union: Symbol('union'),
54+
intersection: Symbol('intersection')
55+
} as {
56+
readonly union: unique symbol
57+
readonly intersection: unique symbol
58+
}
59+
export type SpecialShapes = typeof specialShapes
60+
export interface SpecialShape<
61+
T extends SpecialShapes[keyof SpecialShapes],
62+
S extends readonly Schema<any, any>[] = []
63+
> {
64+
type: T
65+
schemas: S
66+
}
5267
interface SchemaMeta<Shape, T> {
5368
}
5469
interface SchemaMethods<Shape, T> {
@@ -135,7 +150,7 @@ export namespace t {
135150
[Schemas] extends [never]
136151
? Schema<typeof symbols.never, never>
137152
: Schema<
138-
T2I<Schemas>,
153+
SpecialShape<SpecialShapes['intersection'], Schemas>,
139154
T2I<InferT<Schemas>>
140155
>
141156
) : never

0 commit comments

Comments
 (0)