millsp / ts-toolbelt Goto Github PK
View Code? Open in Web Editor NEW๐ท TypeScript's largest type utility library
Home Page: https://millsp.github.io/ts-toolbelt/
License: Apache License 2.0
๐ท TypeScript's largest type utility library
Home Page: https://millsp.github.io/ts-toolbelt/
License: Apache License 2.0
node_modules/ts-toolbelt/out/types/src/Object/P/Omit.d.ts(12,14): error TS2456: Type alias '_Omit' circularly references itself.
node_modules/ts-toolbelt/out/types/src/Object/P/Omit.d.ts(13,91): error TS2315: Type '_Omit' is not generic.
node_modules/ts-toolbelt/out/types/src/Object/P/Omit.d.ts(23,66): error TS2315: Type '_Omit' is not generic.
Typescript: 3.4.3
Prevents compilation
Since 3.8.4 this got broken, it used to work in 3.8.3
class Foo {
readonly s: string = "hi"
}
function someFn<F extends Foo>(foo2: F) {
const wfoo = foo2 as O.Writable<F> // here it complains this is invalid
wfoo.s = "ho" // here it complains s is not there
}
Hi ๐ธ! I have updated the lib to the latest and webpack is showing an error ๐ข
I have installed some other versions to confirm it, after 3.8.19 release, all onwards are broken.
It produces an error because webpack (and I suppose other bundlers) use main entry from package.json
Not fail
Get index.js back in out
folder?
TBH I am not sure if this is a bug with ts-toolbelt
or a limitation of TS itself.
I have a case where I utilise O.Filter
against a generic model.
For basic concrete implementations of the generic model everything works okay, however, if I have a kind of factory to produce various forms of the generic model then the typing breaks.
Given the following...
import { O } from 'ts-toolbelt';
// Filter out Actions - i.e. functions
type State<Model extends object> = O.Filter<Model, Function>;
type Action<Model extends object, T> = (
state: State<Model>,
payload: T,
) => void;
// Basic model with a generic value and setter
interface GenericModel<T> {
value?: T;
set: Action<GenericModel<T>, T>;
}
The following concrete implementation works
const concrete: GenericModel<number> = {
value: 1,
set: (state, payload) => {
// The state.value is correctly typed
// ๐
state.value = payload;
},
};
However, the following generic factory implementation fails
const genericFactory = <T>(): GenericModel<T> => {
return {
value: undefined,
set: (state, payload) => {
// The state.value does not exist
// ๐ฅ
state.value = payload;
},
};
};
I see that there are types like Merge
and MergeUp
and others. What does the Up stand for?
#up
When using a Pick method on a type that contains optional parameters the resulting type will contain all of those optional parameters regardless of what the path argument is.
interface Test {
foo: string;
bar?: string;
}
type pickedTest = O.P.Pick<Test, ['foo']>;
pickedTest
contains foo
and optional bar
property.
Type pickedTest
contains only foo
property.
I want to use it to type GraphQL queries and excluding certain properties is critical for building the returned object type correctly.
Installing ts-toobelt
v4.7.19 when using lib esnext
or es2019
causes compilation error in
node_modules/ts-toolbelt/out/types/src/Tuple/Diff.d.ts:17:96 - error TS2589: Type instantiation is excessively deep and possibly infinite.
17 export declare type Diff<T extends Tuple, T1 extends Tuple, match extends Match = 'default'> = TupleOf<ODiff<ObjectOf<T>, ObjectOf<T1>, match>>;
Installing v4.7.18 does not cause this error
Use this minimal tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2019"],
"strict": true
}
}
TypeScript 3.6.3
Only es2019.array
is enough to trigger the error, although not clear what in https://github.com/microsoft/TypeScript/blob/master/lib/lib.es2019.array.d.ts causes it.
I will be very cool to have a type that inverses a string enum type.
For example
enum Validations {
J1 = 'NoComm',
J2 = 'Normal',
J3 = 'CreditLimit',
J4 = 'AutoComm',
}
type ValidationsType = typeof Validations;
type ValidationsKeys = keyof ValidationsType;
type Minify<T> = {
[K in keyof T]: T[K];
};
type ValidationsReverse: A.Compute<
Minify<
Union.IntersectOf<
{
[key in ValidationsKeys]: {
[key2 in ValidationsType[key]]: key;
};
}[ValidationsKeys]
>
>
>;
/*
ValidationsReverse = {
NoComm: "J1";
Normal: "J2";
CreditLimit: "J3";
AutoComm: "J4";
*/
@pirix-gh what do you think?
T.Writable is not exported.
Currently can only be accessed by
import {Writable} from 'ts-toolbelt/out/types/src/Tuple/Writable';
Add to Tuple/_api.d.ts
export { Writable } from './Writable';
When using switch on types, typescript allows a validation to make sure you covered all the cases in the form of a method that accepts never:
const assertUnreachable = (x: never): never => {
throw new Error(`Didn't expect to get here`);
};
So the following will work:
type T1 = 'a' | 'b';
const f = (v: T1): void => {
switch (v) {
case 'a':
console.log('a');
break;
case 'b':
console.log('b');
break;
default:
assertUnreachable(v);
}
};
But if we change T1
to
type T1 = 'a' | 'b' | 'c';
TS will produce an error. Letting you know you forgot to cover a case.
I know this is not a type, but it is a very handy method that can be widely reused.
@pirix-gh what do you say? If you want to throw it down the stairs I will completely understand :-)
In the APIs I'm working with, I have some nested properties - for example:
type Household = {
id: number
people: {
name: string
age: number
...
}[]
...
};
Some APIs only give a subset of data - in this case, only Household.people.name
- which is a perfect fit for O.P.Pick
. However, O.P.Pick
doesn't support arrays natively, which results in types like these:
// {people: {[x: number]: {name: string}}}
type HouseholdSubset = O.P.Pick<Household, ['people', number, 'name']>;
HouseholdSubset.people
isn't an array, it's still an object indexed by number - so we can't use array methods in HouseholdSubset.people
, and we can't pass it to functions expecting an array. In this case, we'd like to somehow get a type of {people: {name: string}[]}
.
Either modify O.P.Pick
or create a new type which automatically traverses arrays.
I have two solutions in mind:
An example which only works using the first solution:
type FourDimensions = {
freedom: number[][] | {
dive: string
pp: number
}[][][][]
blue: 'zenith'
}
// {freedom: number[][] | {dive: string}[][][][]}
type FreedomDive = O.P.Pick<FourDimensions, ['freedom', 'dive']>
An example which works for both solutions:
type Household = {
id: number
people: string[] | { // string[] added for distributive checking
name: string
age: number
}[]
};
// {people: string[] | {name: string}[]}
type HouseholdSubset = O.P.Pick<Household, ['people', 'name']>
Realistically speaking, most use cases (including all of mine) will only use one dimension, so it may not worth it to require an upgrade to TypeScript 3.7 for a niche feature.
N/A
If modifying O.P.Pick
for this feature, this will be a breaking change for any project using O.P.Pick
to pick into array properties. There should realistically not be any users affected by this, but it is a breaking change nonetheless.
If using the unlimited dimension solution, requiring TypeScript 3.7 will be a huge breaking change for most users.
I implemented this as a PR, using a different name (O.P.PickA) and using the one dimensional implementation. I realised that the details of this should be discussed in an issue before any PRs should be made - so I made this issue ๐
I get type errors unless I set my Typescript to skipLibCheck
. Specifically around the types that operate on keys.
For example Omit
is defined as:
export type Omit<O extends object, K extends string> =
Pick<O, Exclude<keyof O, K>>
Why do you not use a keyof
based restriction?
i.e.
export type Omit<O extends object, K extends keyof O> =
Pick<O, Exclude<keyof O, K>>
You are obviously a Typescript god, so I assume there must be a solid reason for this decision?
Performance of DefinitelyTyped/ramda has suffered significantly with a recent upgrade (which relies on this library heavily now)
Upgrading from @types/[email protected]
to @types/[email protected]
also upgrades ts-toolbelt
from 3.8.4
to 4.7.7
. In my project, which uses Ramda frequently but not in any crazy manner, this one change in my project.json
doubles my project's TS type checking time from ~25sec to ~60-70sec.
There is not such a significant slowdown of type-checking when using this library/ @types/ramda
.
Using the latest TS, 3.6.3
Other libraries under a more permissive license (MIT, APACHE-2) won't be able to use ts-toolbelt
if it stays under AGPL.
The tst
directory is bundled in the npm package and it is redundant...
How do I pick two different paths of an object that have different subobject names, for example:
interface Test {
foo: {
foofoo: string;
};
bar: number;
unneeded: null;
}
type pickedTest = O.P.Pick<Test, ['foo' | 'bar', 'foofoo']>;
I want pickedTest
to be
type pickedTest = {
foo: {
foofoo: string;
};
bar: number;
}
but the resulting type is
type pickedTest = {
foo: {
foofoo: string;
};
}
which kinda makes sense.
What is the expected syntax here and if that's even possible?
I tried to look for a generic DeepReadonly in the library, but I couldn't find one, is there one?
If not, would this be a proper way to implement it?
type DeepReadonly<T> = T extends any[]
? T.Readonly<T, "deep"> extends infer R
? R
: never
: T extends ReadonlyArray<infer D>
? T.Readonly<D[], "deep"> extends infer R
? R
: never
: T extends object
? O.Readonly<T, keyof T, "deep"> extends infer R
? R
: never
: T
also, is the extends infer R ? R : never
required here to avoid getting type instantation too deep errors?
When I read the Equal
type implementation, I have two question.
The first one:
type EqualsDefault<A1 extends any, A2 extends any> = (A1 | A2) extends A1 ? (A1 | A2) extends A2 ? true : false : false;
My question is, why not directly use:
type EqualsDefault<A1 extends any, A2 extends any> = A2 extends A1 ? A1 extends A2 ? true : false : false;
The second one:
EqualsStrict<A1 extends any, A2 extends any> = (<A>() => A extends A1 ? 1 : 2) extends (<A>() => A extends A2 ? 1 : 2) ? true : false;
Why this work for indexed type
includes readonly
case?
See @leebenson's comment on #57.
Something which can do something like this:
type Nested = {
a?: NestedA | NestedA[] | null | number;
z: 'z';
};
type NestedA = {
b?: NestedB | NestedB[] | null | number;
y: 'y';
};
type NestedB = {
c: 'c';
};
// resolves to 'c'
type C = O.P.At<Nested, ['a', 'b', 'c']>;
This follows on from the discussion on #57. From the previous discussion:
This is surprisingly non-trivial when it comes to nested arrays in the path... To do this with infinite nesting, we need a utility type to get the (nested) inner type of an array. However, this is impossible - see this TS Playground for an example of why it's impossible to implement.
However, if we restrict nesting to something absurd like four dimensional nested arrays, it could work, like in this TS Playground.
As an update to that, I believe this feature is impossible to get "perfect", for similar reasons to why getting the nested inner type of an array is impossible. In a type definition, you can't directly refer to the type definition without some sort of indirection - see the TS 3.7 blog post for more details on that.
Here's the code I tried:
type _At<O, Path extends Tuple<Index>, I extends Iteration = IterationOf<'0'>> =
Pos<I> extends Length<Path>
? O // target
: O extends object
? O extends (infer A)[]
? _At<A, Path, I>
: Path[Pos<I>] extends infer K
? K extends keyof O
? _At<O[K], Path, Next<I>>
: never // not key
: never // impossible
: never; // dive into not object
TypeScript 3.7 complains that it's a circularly referenced type. Adding a type Lazy<T> = T
doesn't help either - I think the type system eagerly evaluates all branches of a conditional type at runtime (without expanding nested types like objects and arrays). That means we need to add some level of indirection at every level we traverse.
The other way of doing it is manually "unrolling" the recursion many times, like the ArrayType
example above:
// from
type ArrayType<T> = T extends (infer A)[] ? ArrayType<A> : T;
// to
type ArrayType4<T> = T extends (infer A)[] ? ArrayType3<A> : T;
type ArrayType3<T> = T extends (infer A)[] ? ArrayType2<A> : T;
type ArrayType2<T> = T extends (infer A)[] ? ArrayType1<A> : T;
type ArrayType1<T> = T extends (infer A)[] ? A : T;
If we were to do this in O.P.At, it would result in pretty unreadable code. Instead, we could "wrap" the return value of the unlimited depth O.P.At with something { __wrap: T }
(we want this wrapping to be unique so we don't accidentally unwrap a user's type) and "unwrap" it at the end with something like ArrayType
above.
In fact, I found a way of getting 2n levels of recursion with n "unrolls" (Playground link) - so we can pretty easily get lots of unrolling with little code:
// unrolls (2*3)+1 = 7
type ArrayType7<T> = T extends (infer A)[] ? ArrayType3<ArrayType3<A>> : T;
// unrolls (2*1)+1 = 3
type ArrayType3<T> = T extends (infer A)[] ? ArrayType1<ArrayType1<A>> : T;
// unrolls 1
type ArrayType1<T> = T extends (infer A)[] ? A : T;
// resolves to number[]
type EightDimensions = ArrayType7<number[][][][][][][][]>;
We nest the double-types inside a conditional to prevent TypeScript from expanding the types to the user... and filling their screen with ArrayType1
s ๐.
Using this, we can write some code that works on TypeScript 3.6:
// unwraps 15 { __wrap: T } levels
type Unwrap<T> = T extends { __wrap: infer U } ? Unwrap7<Unwrap7<U>> : T;
type Unwrap7<T> = T extends { __wrap: infer U } ? Unwrap3<Unwrap3<U>> : T;
type Unwrap3<T> = T extends { __wrap: infer U } ? Unwrap1<Unwrap1<U>> : T;
type Unwrap1<T> = T extends { __wrap: infer U } ? U : T;
type _At<O, Path extends Tuple<Index>, I extends Iteration = IterationOf<'0'>> =
Pos<I> extends Length<Path>
? O // target
: O extends object
? O extends (infer A)[]
? { __wrap: _At<A, Path, I> }
: Path[Pos<I>] extends infer K
? K extends keyof O
? { __wrap: _At<O[K], Path, Next<I>> }
: never // not key
: never // impossible
: never; // dive into not object
type At<O extends object, Path extends Tuple<Index>> = Unwrap<_At<O, Path>>
type Nested = {
a?: NestedA | NestedA[] | null | number;
z: 'z';
};
type NestedA = {
b?: NestedB | NestedB[] | null | number;
y: 'y';
};
type NestedB = {
c: 'c';
};
// successfully evaluates to 'c'
type C = At<Nested, ['a', 'b', 'c']>;
@pirix-gh What do you think? The ArrayType
utility type could also come in handy as well, so we may want to add that to ts-toolbox
too.
Hi @pirix-gh, can you please explain the following error?
It seems that typescript is able to realize that
T[O.SelectKeys<T, V>] === V
When used with concrete types, but when using generics, it fails miserably...
interface IA {
a: string;
b: number;
}
const x: IA[O.SelectKeys<IA, string>] = ''; // valid as the type resolves to string
const extractFieldOfType = <T extends object, V>(object: T, field: O.SelectKeys<T, V>): V => {
return object[field];
};
/*
error TS2322: Type 'T[{ 1: keyof T & string; 0: never; }[Extends<T[keyof T & string], V>]] | T[{ 1: keyof T & number; 0: never; }[Extends<T[keyof T & number], V>]] | T[{ 1: keyof T & symbol; 0: never; }[Extends<...>]]' is not assignable to type 'V'.
'T[{ 1: keyof T & string; 0: never; }[Extends<T[keyof T & string], V>]] | T[{ 1: keyof T & number; 0: never; }[Extends<T[keyof T & number], V>]] | T[{ 1: keyof T & symbol; 0: never; }[Extends<...>]]' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint '{}'.
Type 'T[{ 1: keyof T & string; 0: never; }[Extends<T[keyof T & string], V>]]' is not assignable to type 'V'.
'T[{ 1: keyof T & string; 0: never; }[Extends<T[keyof T & string], V>]]' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint '{}'.
Type 'T[{ 1: keyof T & string; 0: never; }[0 | (T[keyof T & string] extends V ? 1 : 0)]]' is not assignable to type 'V'.
'T[{ 1: keyof T & string; 0: never; }[0 | (T[keyof T & string] extends V ? 1 : 0)]]' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint '{}'.
Type 'T[{ 1: keyof T & string; 0: never; }[T[keyof T & string] extends V ? 1 : 0]]' is not assignable to type 'V'.
'T[{ 1: keyof T & string; 0: never; }[T[keyof T & string] extends V ? 1 : 0]]' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint '{}'.
Type 'T[{ 1: keyof T & string; 0: never; }[T[string] extends V ? 1 : 0]]' is not assignable to type 'V'.
'T[{ 1: keyof T & string; 0: never; }[T[string] extends V ? 1 : 0]]' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint '{}'.
Type 'T[keyof T & string]' is not assignable to type 'V'.
'T[keyof T & string]' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint '{}'.
Type 'T[string]' is not assignable to type 'V'.
'T[string]' is assignable to the constraint of type 'V', but 'V' could be instantiated with a different subtype of constraint '{}'.
/*
Is there a way with ts-toolbelt
to recursively add a specific property to all nested objects of a given type?
Example of what I am trying to do:
interface Address {
city: string
country: string
}
interface User {
id: string
name: string
address: Address
}
interface Session {
id: string
user: User
}
Convert this to:
interface SessionTypenames {
session: {
__typename: string // new property
id: string
user: {
__typename: string // new property
id: string
name: string
address: {
__typename: string // new property
city: string
country: string
}
}
}
This would be very helpful in defining mocked data from server when using Apollo's MockedProvider which requires adding this data to the mocked data, but not defined by the original graphql schema types.
#typescript #graphql #apollo-client
I see that I can import O.P
or Object.P
and I want to know what it is
#Object.P #Deep #Path
It will be a really nice addition to add a require only one
Object type.
The basic idea behind it is that given an Object with multiple keys, create a union type in a way that a sub set of keys from the object can only exists on its own with out the others.
E.G
interface I {
y: number;
x1: string;
x2: number;
}
type IUnion = RequireOnlyOne<I, 'x1' | 'x2'>;
/*
{
y: number;
x1: string;
} |
{
y: number;
x2: number;
}
*/
I currently achieve that type using the following:
type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys];
@pirix-gh what do you think?
I'm trying to rewrite the entire typings for underscore in the @typings/underscore library.
One of the methods is map and one of it's variations is to take a list and map every object in it to the value of the specified path of that object. e.g.
_.map( [{arr: [5, 1, 7]}, {arr: [3, 2, 1]}], ['arr']); // should be [[5, 1, 7],[3, 2,1]]
The best I got so far is
interface List<T> {
[index: number]: T;
length: number;
}
type TypeOfList<T> = T extends List<infer V> ? V : any;
map<T extends TypeOfList<V>, K extends string[], V extends List<any> = List<T>>(
list: V,
iterator: A.Cast<K, O.PathValid<T, K>>,
context?: any): Array<U.Nullable<O.Path<T, K>>>;
But when I try the example above I get
Typescript type instantiation is excessively deep and possibly infinite
@pirix-gh can you please help?
Hi @pirix-gh
First I'm glad to see you finally released v3!
I have the following problem: (using v3)
import { Object, Any, Union } from 'ts-toolbelt';
export function getProp<O extends object, P extends Array<string | number>>(
obj: Union.Nullable<O>,
...keys: Any.Cast<P, Object.PathValid<O, P>>
): Object.Path<O, P> | undefined {
return keys.reduce(
(result: any, key: string | number | symbol) => (result === null || result === undefined ? undefined : result[key]),
obj,
);
}
interface IInterface {
a: {
b: string[];
};
}
type elem = IInterface['a']['b'][1]; // string
const a: IInterface = undefined as any;
const res = getProp(a, 'a', 'b'); // string[] | undefined
const res = getProp(a, 'a', 'b', 0); // TS2345: Argument of type '0' is not assignable to parameter of type 'never'.
Any thoughts?
How do I Use Object.TupleOf
type objectToTuple = {
one: string
two: string
};
type tuple = Object.TupleOf<objectToTuple>
const a: tuple = []
I get
Type instantiation is excessively deep and possibly infinite.
and the type of tuple
is
type tuple = []
I've stuck with React
, Typescript
, and Ramda
. I can't add a correct type for currying. Seems like ramda use underhood ts-toolbelt
to add typing for a currying.
I've got an error with isElementIcon(refIcon)
const isElementIcon: <[React.RefObject<HTMLDivElement>]>(args_0: React.RefObject<HTMLDivElement>) => Curry<(target: EventTargetOrNull) => boolean>
Type 'Curry<(target: EventTargetOrNull) => boolean>' is not assignable to type 'SafePred<EventTargetOrNull>'.
Types of parameters 'target' and 'a' are incompatible.
Type 'EventTargetOrNull' is not assignable to type 'Node | EventTarget | Type<"x", "x">'.
Type 'null' is not assignable to type 'Node | EventTarget | Type<"x", "x">'.ts(2322)
type EventTargetOrNull = Node | EventTarget | null
const [isOpen, setIsOpen] = useState(false)
const refIcon = useRef<HTMLDivElement>(null)
const isElementIcon = curry<(ref: RefObject<HTMLDivElement>, target: EventTargetOrNull) => boolean>((ref, target) => {
return ref.current !== null && (ref.current === target || ref.current.contains((target as unknown) as Node))
})
const handleClick = pipe<MouseEvent | TouchEvent, EventTargetOrNull, void>(
prop('target'),
cond<EventTargetOrNull, void>([
[isElementIcon(refIcon), () => setIsOpen(!isOpen)],
[isOutsideWrapperEvent(refWrapper), hideTooltip]
])
)
I've pushed a pre-release npm i ts-toolbelt@next --save
with the following changes:
Breaking changes:
N.Clamp
boolean
now return 0 | 1
(still need to update the docs)Boolean
(B) now only accepts 0 | 1
as a boolean
with possibility to transform to a real boolean
with Fmt
Changes:
extends
Boolean
internallyI've removed a lot of <x> extends infer X
so you might encounter "type instantiation is excessively deep..." when combining types together, but I haven't encountered any in the tests though. If necessary, I'll write more tests.
I would appreciate if you can install the next
version and give me your feedback ๐
Thanks,
(@regevbr)
I am using Microsoft DevOps Feed and reference npmjs as an upstream package source. When I try to "npm install ts-toolbelt", I got following errors:
{"success":"false","error":"BadGateway","reason":"Error encountered fetching upstream data. A configured upstream registry failed: {Name = npmjs, Location = https://registry.npmjs.org/, Protocol = npm, UpstreamSourceType = Public ExceptionType = Microsoft.VisualStudio.Services.Packaging.ServiceShared.Versioning.InvalidSemanticVersionException, ExceptionMessage = The version 3.8.1567781323189 is invalid.}"}
Not for sure (I am checking with Microsoft support) but I guess the reason for this problem is that the patch version is not incremental strictly:
70 < 1567788970295 < 1567788972460 > 69
Here is the Semantic Versioning Spec.
Could you help unpublish the version numbers that are not incremented?
The provided utilities will no longer handle unions by default, unless the utility itself is designed to handle them. This means that type distribution is not implicitly handled no longer.
Why?
This library needs to stay 1:1 with TypeScript, and TS does not distribute types by default (I think of the base utility keyof
). keyof
is one of the cornerstones of TS, used everywhere within the library. So it would make sense that we keep logic on this level. I did try to make type distribution the default
, meaning that the exposed types work with union
s every time, but then I was left with a few doubts:
X extends unknown ? do something with X : never
Should types like
At
be able to operate on unions, or not, how?
extends
& mapped
In the end, I found this pretty confusing & decided to stop it to stay as close to TS as we can. But what is nice is that we will still be able to handle union
s. Read below to see how.
This leaves unaffected:
This affects types like:
keyof
-like behaviorsFrom now on, if you need to deal with unions with those types, please look for their ...Up counterpart (eg. MergeUp
). If you can't find it, please open a feature request.
And it always affected:
Recursive types don't deal well with unions, in general. So if you need a type that does not work with unions, you'll need to distribute it. Like so:
type result = Union extends unknown // let's say `Union` is your union
? UtilityType<Union> // members of `Union` get distributed
: never // Where `UtilityType` can be anything
// that is provided by this library
or the shorter
type result = Union extends any ? UtilityType<Union> : never
Sometimes, this might not work out of the box because it is a recursive type, this is when we need to create the
...Up
counterpart of that type.
Hi, Guy, I read your bog which post in medium.
And I try to improve the type of path
function of Ramda
,
but not work, I spent few hours to fix it, however...
export type Head<T extends ReadonlyArray<any>> = T extends [any, ...any[]]
? T[0]
: never;
export type Tail<T extends ReadonlyArray<any>> = ((...t: T) => any) extends ((
_: any,
...tail: infer TT
) => any)
? TT
: [];
export type HasTail<T extends ReadonlyArray<any>> = T extends ([] | [any])
? false
: true;
export type GetByProp<P extends keyof O, O> = O[P];
export type GetByPath<
P extends ReadonlyArray<number | string>,
O extends object | number
> = {
0: GetByPath<Tail<P>, GetByProp<Head<P>, O>>;
1: GetByProp<Head<P>, O>;
}[HasTail<P> extends true ? 0 : 1];
declare const f: <
P extends ReadonlyArray<number | string>,
O extends object | ReadonlyArray<any>
>(
p: P,
o: O
) => GetByPath<P, O>;
// T is 1
type T = GetByPath<['a'],{a:1}>
// v:never
const v = f(['a'] as const, { a: 1 });
As you can see, the T is type 1
, and the v
infer as never
.
Why? Pls. Thank you.
In the lib's package.json
the main
field points to a index.js
file that does not exist when the lib is installed in a project. Because of this, in the project, eslint complains it cannot resolve the import.
The lib should have an index.js
file, even if it is empty, so that linters and other intellisense tools can find the file which they are directed to by the package.json
main
field.
Since keys of an object can be of type string|number|symbol
and sometimes types require only string values, it will be handy to have a type that extracts only the string key (or of any type for that matter)
type Index = string | number | symbol; // I think we should make that type public!
type KeysOfType<T extends object, M extends Index> = Extract<keyof T, M>;
type t = KeysOfType<{
a: number;
2: number;
}, string>; // "a"
@pirix-gh thoughts?
Extends<any, any> does not return true
type test0 = Extends<any, any> // boolean
type test0 = Extends<any, any> // true
@regevbr discovered this bug microsoft/TypeScript#30188 (comment)
Is there something like O.Nullable
but that actually makes keys turn into V | null
?
e.g.
interface A { x: number; y: number }
type AN = O.NullNullable<A, "x">
// AN is { x: number | null; y: number }
Union types tend to malfunction when used with recursive types for reasons that are internal to TypeScript and this library. But you can make it work, just distribute your union as needed:
Union extends unknown // let's say `Union` is your union
? RecursiveType<Union> // it gets distributed to another type
: never
...And if this still doesn't work, then it's probably because the recursive type needs to do this deeply. In that case, you're welcome to open an issue, and I'll bring a Up
version of that type out.
#broken #union #recursive
Package has "main" field
https://github.com/pirix-gh/ts-toolbelt/blob/master/package.json#L26
but this file doesn't exist. I think thats why eslint triggers error Unable to resolve path to module 'ts-toolbelt' import/no-unresolved
.
I create this file manually by touch ./node_modules/ts-toolbelt/out/index.js
and the error is gone
How to use this module? Is it possible used with ramda?
Q1: How to simply create a type for a curry function?
Like this add function
const add = (x: number) => (y: number) => x + y
I know we can create a function type and turn it to curry.
type add = F.Function<[ number, number], number>
declare function curry<Fns extends F.Function>(fns: Fns): F.Curry<Fns>
const add = (x: number, y: number): number => x + y
const addCurry = curry(add)
const c = addCurry(1)(2)
Is there a more simply way?
Q2: Is this module work with ramda?
ramda curry type is not so friendly in typescript
R.map(x => x + 1)([1,2,3]) // error, x: {}, Operator '+' cannot be applied to types '{}' and '1'
Is there a way to change ramda function type use this module?
Thanks
Hello, it seems that I have a wrong type inference with [email protected] and [email protected] when using the following:
const append = curry(<A>(item: A, list: A[]) => list.concat([item]))
const appendA = append('a')
// type is `(list: unknown[]) => unknown[]` instead of `(list: string[]) => string[]`
// type could also look like `(list: {}[]) => {}[]` depending on TS and/or IDE...
I made a small project on codesandbox.io so you can check by yourselves: click here to see it.
Is type inference supposed to work with F.Curry and generic functions?
#typescript #curry #toolbelt #generic #function #parametrized #type #inference #not #working
The exports are empty. Does not matter if I use import { O } ...
or import tb ...
happens in both [email protected] and [email protected]
npx create-react-app tb-test --typescript
cd tb-test
yarn add ts-toolbelt
code .
Maybe these should be exported? right now it is export {};
the export declare type
does not seem to be picked up.
Maybe this only happens in CRA. ๐ค
I changed the default tsconfig.json in CRA to the one that is used in this project and the imports still did not work.
Hello and thanks for creating this package! ๐โโ๏ธ I'm evaluating ts utility packages and this looks like a solid option, but the docs are missing examples.
The docs have everything necessary, except for examples.
Each function/type should have at least one example of how it can be used. Lodash and Ramda docs get it right, for example.
None. Examples are necessary to help people understand what each function/type/utility does in a clear manner. They are indispensable.
Examples:
What is the best way to transform this
type A = {id: string}
type B = {uuid: string} | {name: string}
to something like this
type C = {
id: string;
uuid: string;
} | {
id: string;
name: string;
}
merge unions
How to write the type of an async pipe function?
Implementation
const pipePromises = (...fns :any) => (x:any) => fns.reduce((p:any, fn:any) => p.then(fn), Promise.resolve(x));
const a = (a1: number) => `${a1}`;
const b = async (a1: string) => [a1];
pipePromises(a, b)(1).then((v: any) => console.log(v)); // ["1"]
pipePromises
accepts normal function and async function. I know that the normal pipe function can use <Fns extends F.Function[]>(...args: F.Piper<Fns>): F.Pipe<Fns>
but I don't know how to write the promise version.
#promise, #async
The whole codebase is full of <x> extends infer X ? <y>: never
#extends #infer #lots #used
The clutter has been removed out of the documentation site. It previously had mixed internals as well as the exposed API, which was confusing.
class M {
fn(): void {}
x = 5
}
function f<T extends M>(m: T) {
const w = m as O.Writable<T>
w.fn() // complains fn is not there
w.x = 10 // complains x is not there
}
works ok with the latest version of 3
Does v4 require TS 3.6 or it still works with TS3.5?
The Number
utilities all expect receiving a number that is a string
. That does not make sense.
#number #string #input
This one: https://pirix-gh.github.io/ts-toolbelt/modules/_string_format_.html
An example would be welcome.
#format
Callback are sadly still very common, no reason why we should not type it properly :-)
type Callback<T> = ((error: Error, result?: never) => void) & ((error: undefined | null, result: T) => void);
function callback(error: undefined | null, result: string): void;
function callback(error: Error, result?: never): void;
function callback(error: Error | undefined | null, result?: string): void {
return;
}
function f(cb: Callback<string>): void {
cb(new Error('a'));
cb(undefined, 'd');
cb(new Error('a'), 's'); // produces error
cb(undefined); // produces error
}
f(callback);
@pirix-gh thoughts?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.