ts-deep-pick
v0.2.2
Published
TypeScript utility generating new types by deep picking/omitting, leveraging Template Literal Types from TypeScript >= 4.1
Downloads
27,399
Maintainers
Readme
ts-deep-pick
is a TypeScript type-only utility package that allows for
type-safe, deep picking/omitting of properties from nested object types.
Powered by the
template literal type released in TypeScript 4.1,
the type DeepPick
will offer all combinations of nested pick/omit string
patterns that will then return the deeply picked/omit type according to the
deep-picking grammar.
Usage
DeepPick
import { DeepPick } from "ts-deep-pick";
interface FooBar {
foo: ABC;
bar: ABC;
}
interface ABC {
a: number;
b: number;
c: number;
}
// Result type: { foo: { a: number } };
// including a property "positively" omits the exluded properties.
type FooWithJustA = DeepPick<FooBar, "foo.a">; // "." joins nested properties
// Result type: { foo: { a: number, b: number } };
type FooWithJustAB = DeepPick<FooBar, "foo.a" | "foo.b">;properties
// Result type: { foo: { a: number, b: number } };
type FooWithoutC = DeepPick<FooBar, "foo.!c">; // "!" omits a property
// Result type: { bar: ABC };
type Bar = DeepPick<FooBar, "!bar">; // "!" omits a property
// Result type:
// {
// foo: { a: number, b: number },
// bar: ABC
// };
// "~" mutates the property's nested membership without explicitly picking
// the property and thereby omitting the remaining.
type FooBarWithoutFooC = DeepPick<FooBar, "~foo.!c">;
// Result type: Array<{ foo: ABC }>
type ArrayWithFoo = DeepPick<FooBar[], "[].foo"> // "[]" accesses an array item.
// Result type: FooBar
type ArrayWithFoo = DeepPick<FooBar, "*"> // "*" is the "identity" operand
DeepPickPath
Returns the possible path that can be used to passed into DeepPick
for a given
structure.
import { DeepPickPath } from "ts-deep-pick";
// assume FooBar as defined earlier.
// "foo" | "bar" | "!foo" | "!bar" | "~foo.a" | "~foo.!a" | ...
type Path = DeepPickPath<FooBar>;
Behavior
- Union paths are treated like a combination of paths. For example,
"a"|"b"
would mean "pick propertiesa
andb
". - At each level, the "picked set" is determined before omitting and mutating.
If the paths at the given level include at least one explicitly picked
key (
"a"
) then these keys and the mutations ("~a"
) become the picked set. In this case, omits are redundant because they are either no-op (omitting keys that aren't picked) or they make the picks no-op (omitting keys that are picked). - If the paths at the given level consist only of omits (
"!a"
) and mutations ("~a"
), then the omit operation takes place on the full set of keys.
Examples
"a" | "b"
will pick just propertiesa
andb
."a" | "!a"
will pick nothing (a
is picked but is then omitted.)"~a.foo"
will pick everything at the outermost level, and pick foo as the only property ofa
."!a" | "~b.foo"
will pick everything except fora
at the outermost level, and modifiesb
such that onlyfoo
is available there.
Grammar
The character tokens .
, !
, ~
, []
and *
define the property
separator, omit prefix, mutation prefix, array index property and the
pass-through (return the original type) operand respectively. These tokens can
be replaced by defining the following interface and passing it as the extra
type parameter to DeepPick
and DeepPickPath
.
interface DeepPickGrammar {
array: string; // default: "[]"
prop: string; // default: "."
omit: string; // default: "!"
mutate: string; // default: "~"
glob: string; // default: "*"
}
Example:
import { DefaultGrammar } from "ts-deep-pick";
interface G extends DeepPickGrammar {
props: ":"; // Now use ":" as property separator
}
// Result type: { foo: { a: number } };
type FooWithJustA = DeepPick<FooBar, "foo:a", G>;
AmbiguousProp<G = DefaultGrammar>
Returns the template literal type that represents any string that -- if used as a property name -- would be ambigous given the set of character tokens.
// Result type: "a"
// ("!a" is ambiguous because of the "!" character).
// ("a.b" is ambiguous because of the "." character)
type ValidProps = Exclude<"a" | "!a" | "a.b", AmbiguousProps>
// Result type: "a" | "a.b"
// ("a:b" would have been ambiguous now because of custom grammar G defined
// earlier).
type ValidProps = Exclude<"a" | "!a" | "a.b", AmbiguousProps<G>>