Skip to content

Commit

Permalink
fix: Add a type-checking fast path for primitive types (#755)
Browse files Browse the repository at this point in the history
When `Draft<T>` is applied to a large enum type, TypeScript has to do a
lot of unnecessary structural comparisons to confirm that no element of
the enum matches `Function`, `Date`, `RegExp`, etc.  Determining that
they do match `string` or `number`, on the other hand, is trivial.  This
change splits `PrimitiveType` out of `AtomicObject` so that the fast
path can be checked first.

In microsoft/TypeScript#42824, this cut the
check time from ~2.5 seconds to ~0.3 seconds.
  • Loading branch information
amcasey committed Mar 17, 2021
1 parent 3b4286d commit d395efe
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 15 deletions.
13 changes: 8 additions & 5 deletions compat/pre-3.7/dist/immer.d.ts
Expand Up @@ -5,6 +5,8 @@ type Tail<T extends any[]> = ((...t: T) => any) extends (
? TT
: []

type PrimitiveType = number | string | boolean

/** Object types that should never be mapped */
type AtomicObject =
| Function
Expand All @@ -13,11 +15,10 @@ type AtomicObject =
| Promise<any>
| Date
| RegExp
| Boolean
| Number
| String

export type Draft<T> = T extends AtomicObject
export type Draft<T> = T extends PrimitiveType
? T
: T extends AtomicObject
? T
: T extends Map<infer K, infer V>
? DraftMap<K, V>
Expand All @@ -34,7 +35,9 @@ interface DraftMap<K, V> extends Map<Draft<K>, Draft<V>> {}
interface DraftSet<V> extends Set<Draft<V>> {}

/** Convert a mutable type into a readonly type */
export type Immutable<T> = T extends AtomicObject
export type Immutable<T> = T extends PrimitiveType
? T
: T extends AtomicObject
? T
: T extends Map<infer K, infer V> // Ideally, but wait for TS 3.7: ? Omit<ImmutableMap<K, V>, "set" | "delete" | "clear">
? ImmutableMap<K, V>
Expand Down
19 changes: 9 additions & 10 deletions src/types/types-external.ts
Expand Up @@ -7,15 +7,10 @@ type Tail<T extends any[]> = ((...t: T) => any) extends (
? TT
: []

type PrimitiveType = number | string | boolean

/** Object types that should never be mapped */
type AtomicObject =
| Function
| Promise<any>
| Date
| RegExp
| Boolean
| Number
| String
type AtomicObject = Function | Promise<any> | Date | RegExp

/**
* If the lib "ES2105.collections" is not included in tsconfig.json,
Expand All @@ -42,7 +37,9 @@ type WeakReferences = IfAvailable<WeakMap<any, any>> | IfAvailable<WeakSet<any>>

export type WritableDraft<T> = {-readonly [K in keyof T]: Draft<T[K]>}

export type Draft<T> = T extends AtomicObject
export type Draft<T> = T extends PrimitiveType
? T
: T extends AtomicObject
? T
: T extends IfAvailable<ReadonlyMap<infer K, infer V>> // Map extends ReadonlyMap
? Map<Draft<K>, Draft<V>>
Expand All @@ -55,7 +52,9 @@ export type Draft<T> = T extends AtomicObject
: T

/** Convert a mutable type into a readonly type */
export type Immutable<T> = T extends AtomicObject
export type Immutable<T> = T extends PrimitiveType
? T
: T extends AtomicObject
? T
: T extends IfAvailable<ReadonlyMap<infer K, infer V>> // Map extends ReadonlyMap
? ReadonlyMap<Immutable<K>, Immutable<V>>
Expand Down

0 comments on commit d395efe

Please sign in to comment.