diff --git a/index.d.ts b/index.d.ts index e1cc79d8d..83204bda4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -55,6 +55,7 @@ export type {Jsonify} from './source/jsonify'; export type {Jsonifiable} from './source/jsonifiable'; export type {Schema} from './source/schema'; export type {LiteralToPrimitive} from './source/literal-to-primitive'; +export type {LiteralToPrimitiveDeep} from './source/literal-to-primitive-deep'; export type { PositiveInfinity, NegativeInfinity, diff --git a/readme.md b/readme.md index cbbf828f2..568548fcf 100644 --- a/readme.md +++ b/readme.md @@ -157,6 +157,7 @@ Click the type names for complete docs. - [`ConditionalExcept`](source/conditional-except.d.ts) - Like `Omit` except it removes properties from a shape where the values extend the given `Condition` type. - [`UnionToIntersection`](source/union-to-intersection.d.ts) - Convert a union type to an intersection type. - [`LiteralToPrimitive`](source/literal-to-primitive.d.ts) - Convert a [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) to the [primitive type](source/primitive.d.ts) it belongs to. +- [`LiteralToPrimitiveDeep`](source/literal-to-primitive-deep.d.ts) - Like `LiteralToPrimitive` except it converts literal types inside an object or array deeply. - [`Stringified`](source/stringified.d.ts) - Create a type with the keys of the given type changed to `string` type. - [`IterableElement`](source/iterable-element.d.ts) - Get the element type of an `Iterable`/`AsyncIterable`. For example, an array or a generator. - [`Entry`](source/entry.d.ts) - Create a type that represents the type of an entry of a collection. diff --git a/source/literal-to-primitive-deep.d.ts b/source/literal-to-primitive-deep.d.ts new file mode 100644 index 000000000..e36ff04a9 --- /dev/null +++ b/source/literal-to-primitive-deep.d.ts @@ -0,0 +1,36 @@ +import type {LiteralToPrimitive} from './literal-to-primitive'; +import type {OmitIndexSignature} from './omit-index-signature'; + +/** +Like `LiteralToPrimitive` except it converts literal types inside an object or array deeply. + +For example, given a constant object, it returns a new object type with the same keys but with all the values converted to primitives. + +@see LiteralToPrimitive + +Use-case: Deal with data that is imported from a JSON file. + +@example +``` +import type {LiteralToPrimitiveDeep, TsConfigJson} from 'type-fest'; +import tsconfig from 'path/to/tsconfig.json'; + +function doSomethingWithTSConfig(config: LiteralToPrimitiveDeep) { ... } + +// No casting is needed to pass the type check +doSomethingWithTSConfig(tsconfig); + +// If LiteralToPrimitiveDeep is not used, you need to cast the imported data like this: +doSomethingWithTSConfig(tsconfig as TsConfigJson); +``` + +@category Type +@category Object +*/ +export type LiteralToPrimitiveDeep = T extends object + ? T extends Array + ? Array> + : { + [K in keyof OmitIndexSignature]: LiteralToPrimitiveDeep; + } + : LiteralToPrimitive; diff --git a/test-d/literal-to-primitive-deep.ts b/test-d/literal-to-primitive-deep.ts new file mode 100644 index 000000000..735897e97 --- /dev/null +++ b/test-d/literal-to-primitive-deep.ts @@ -0,0 +1,54 @@ +import {expectType} from 'tsd'; +import type {IsEqual, LiteralToPrimitiveDeep} from '../index'; + +type LiteralObject = { + a: string; + b: number; + c: boolean; + d: { + e: bigint; + f: symbol; + g: { + h: string[]; + i: { + j: boolean; + k: { + l: 1; + m: 'hello'; + o: [1, 2, 3]; + p: ['a', 'b', 'c']; + q: [1, 'a', true]; + }; + }; + }; + }; +}; + +type PrimitiveObject = { + a: string; + b: number; + c: boolean; + d: { + e: bigint; + f: symbol; + g: { + h: string[]; + i: { + j: boolean; + k: { + l: number; + m: string; + o: number[]; + p: string[]; + q: Array; + }; + }; + }; + }; +}; + +const typeEqual: IsEqual< +LiteralToPrimitiveDeep, +PrimitiveObject +> = true; +expectType(typeEqual);