-
-
Notifications
You must be signed in to change notification settings - Fork 571
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0e0a32c
commit 22c3a99
Showing
4 changed files
with
106 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import {Primitive} from './basic'; | ||
|
||
/** | ||
Convert `object`s, `Map`s, `Set`s, and `Array`s and all of their properties/elements into immutable structures recursively. | ||
This is useful when a deeply nested structure needs to be exposed as completely immutable, for example, an imported JSON module or when receiving an API response that is passed around. | ||
Please upvote [this issue](https://github.com/microsoft/TypeScript/issues/13923) if you want to have this type as a built-in in TypeScript. | ||
@example | ||
``` | ||
// data.json | ||
{ | ||
"foo": ["bar"] | ||
} | ||
// main.ts | ||
import {ReadonlyDeep} from 'type-fest'; | ||
import dataJson = require('./data.json'); | ||
const data: ReadonlyDeep<typeof dataJson> = dataJson; | ||
export default data; | ||
// test.ts | ||
import data from './main'; | ||
data.foo.push('bar'); | ||
//=> error TS2339: Property 'push' does not exist on type 'readonly string[]' | ||
``` | ||
*/ | ||
export type ReadonlyDeep<T> = T extends Primitive | ((...arguments: any[]) => unknown) | ||
? T | ||
: T extends ReadonlyMap<infer KeyType, infer ValueType> | ||
? ReadonlyMapDeep<KeyType, ValueType> | ||
: T extends ReadonlySet<infer ItemType> | ||
? ReadonlySetDeep<ItemType> | ||
: T extends object | ||
? ReadonlyObjectDeep<T> | ||
: unknown; | ||
|
||
/** | ||
Same as `ReadonlyDeep`, but accepts only `ReadonlyMap`s as inputs. Internal helper for `ReadonlyDeep`. | ||
*/ | ||
interface ReadonlyMapDeep<KeyType, ValueType> | ||
extends ReadonlyMap<ReadonlyDeep<KeyType>, ReadonlyDeep<ValueType>> {} | ||
|
||
/** | ||
Same as `ReadonlyDeep`, but accepts only `ReadonlySet`s as inputs. Internal helper for `ReadonlyDeep`. | ||
*/ | ||
interface ReadonlySetDeep<ItemType> | ||
extends ReadonlySet<ReadonlyDeep<ItemType>> {} | ||
|
||
/** | ||
Same as `ReadonlyDeep`, but accepts only `object`s as inputs. Internal helper for `ReadonlyDeep`. | ||
*/ | ||
type ReadonlyObjectDeep<ObjectType extends object> = { | ||
readonly [PropertyType in keyof ObjectType]: ReadonlyDeep<ObjectType[PropertyType]> | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import {expectType, expectError} from 'tsd'; | ||
import {ReadonlyDeep} from '../source/readonly-deep'; | ||
|
||
const data = { | ||
object: { | ||
foo: 'bar' | ||
}, | ||
fn: (_: string) => true, | ||
string: 'foo', | ||
number: 1, | ||
boolean: false, | ||
symbol: Symbol('test'), | ||
null: null, | ||
undefined: undefined, // eslint-disable-line object-shorthand | ||
map: new Map<string, string>(), | ||
set: new Set<string>(), | ||
array: ['foo'], | ||
tuple: ['foo'] as ['foo'], | ||
readonlyMap: new Map<string, string>() as ReadonlyMap<string, string>, | ||
readonlySet: new Set<string>() as ReadonlySet<string>, | ||
readonlyArray: ['foo'] as ReadonlyArray<string>, | ||
readonlyTuple: ['foo'] as const | ||
}; | ||
|
||
const readonlyData: ReadonlyDeep<typeof data> = data; | ||
|
||
readonlyData.fn('foo'); | ||
|
||
expectError((readonlyData.string = 'bar')); | ||
expectType<{readonly foo: string}>(readonlyData.object); | ||
expectType<string>(readonlyData.string); | ||
expectType<number>(readonlyData.number); | ||
expectType<boolean>(readonlyData.boolean); | ||
expectType<symbol>(readonlyData.symbol); | ||
expectType<null>(readonlyData.null); | ||
expectType<undefined>(readonlyData.undefined); | ||
expectType<ReadonlyMap<string, string>>(readonlyData.map); | ||
expectType<ReadonlySet<string>>(readonlyData.set); | ||
expectType<readonly string[]>(readonlyData.array); | ||
expectType<readonly ['foo']>(readonlyData.tuple); | ||
expectType<ReadonlyMap<string, string>>(readonlyData.readonlyMap); | ||
expectType<ReadonlySet<string>>(readonlyData.readonlySet); | ||
expectType<readonly string[]>(readonlyData.readonlyArray); | ||
expectType<readonly ['foo']>(readonlyData.readonlyTuple); |