Giter Site home page Giter Site logo

millsp / ts-toolbelt Goto Github PK

View Code? Open in Web Editor NEW
6.6K 29.0 147.0 39.1 MB

๐Ÿ‘ท TypeScript's largest type utility library

Home Page: https://millsp.github.io/ts-toolbelt/

License: Apache License 2.0

TypeScript 98.99% Shell 1.01%
typescript types typescript-library type-checking dynamic-types type-utilities type-safety utility safety

ts-toolbelt's People

Contributors

andarist avatar artalar avatar eranor avatar erdmko avatar kimamula avatar ksxgithub avatar mcpower avatar mesqueeb avatar millsp avatar phated avatar piotrwitek avatar regevbr avatar ruizb avatar thejohnfreeman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ts-toolbelt's Issues

TS2315: Type '_Omit' is not generic and TS2456: Type alias '_Omit' circularly references itself.

๐Ÿž Bug Report

Describe the bug

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.

Reproduce the bug

Typescript: 3.4.3

Expected behavior

Prevents compilation

O.Writable doesn't work well with generics after update to 3.8.4

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
    }

Build is broken in >3.8.19

๐Ÿž Bug Report

Describe the bug

Hi ๐Ÿ˜ธ! I have updated the lib to the latest and webpack is showing an error ๐Ÿ˜ข

In <3.8.19
image

In >3.8.19
image

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
image
image

Expected behavior

Not fail

Possible Solution

Get index.js back in out folder?

Filter not working with generics

๐Ÿž Bug Report

Describe the bug

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.

Reproduce the bug

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;
    },
  };
};

Additional context

[email protected]
[email protected]

Pick method also includes all of the optional parameters

๐Ÿž Bug Report

Describe the bug

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.

Reproduce the bug

interface Test {
  foo: string;
  bar?: string;
}

type pickedTest = O.P.Pick<Test, ['foo']>;

pickedTest contains foo and optional bar property.

Expected behavior

Type pickedTest contains only foo property.

Additional context

I want to use it to type GraphQL queries and excluding certain properties is critical for building the returned object type correctly.

Broken compilation when using es2019 lib

๐Ÿž Bug Report

Describe the bug

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

Reproduce the bug

Use this minimal tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": ["es2019"],
    "strict": true
  }
}

Additional context

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.

Reverse type for enums

๐Ÿฉ Feature Request

Describe the solution you'd like

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

๐Ÿž Bug Report

Describe the bug

T.Writable is not exported.
Currently can only be accessed by

import {Writable} from 'ts-toolbelt/out/types/src/Tuple/Writable';

Possible Solution

Add to Tuple/_api.d.ts

export { Writable } from './Writable';

add assertUnreachable method (not type...)

๐Ÿฉ Feature Request

Describe the solution you'd like

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 :-)

O.P.Pick from arrays

๐Ÿฉ Feature Request

Is your feature request related to a problem?

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}[]}.

Describe the solution you'd like

Either modify O.P.Pick or create a new type which automatically traverses arrays.

I have two solutions in mind:

  • requires TypeScript 3.7 for recursive type references, but supports infinitely nested arrays / unlimited dimensional arrays - see the implementation
  • does not require TypeScript 3.7, but only supports arrays up to a specific dimension - see the implementation for one dimensional arrays. The implementation can be expanded for additional dimensions if desired.

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.

Describe alternatives you've considered

N/A

Teachability, Documentation, Adoption, Migration Strategy

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.

Notes

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 ๐Ÿ˜„

Why using `string` rather than `keyof O` in Omit/Pick/etc?

๐Ÿค” Question

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 issues with this lib and ramda

๐Ÿž Bug Report

Performance of DefinitelyTyped/ramda has suffered significantly with a recent upgrade (which relies on this library heavily now)

Describe the bug

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.

Expected behavior

There is not such a significant slowdown of type-checking when using this library/ @types/ramda.

Additional context

Using the latest TS, 3.6.3

Pick two different branch of an object that have different subobject names

๐Ÿค” Question

Describe your question

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?

DeepReadonly for any type

๐Ÿค” Question

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?

How `Equal` type work?

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?

O.Path improvements (distribution, arrays)

๐Ÿฉ Feature Request

Is your feature request related to a problem?

See @leebenson's comment on #57.

Describe the solution you'd like

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 ArrayType1s ๐Ÿ˜›.

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.

Strange behaviour in generics

๐Ÿค” Question

Describe your question

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 '{}'.

/*

Recursively add property to nested objects of type

๐Ÿค” Question

Describe your question

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.

Search tags, topics

#typescript #graphql #apollo-client

What does `Object.P` stand for?

๐Ÿค” Question

Describe your question

I see that I can import O.P or Object.P and I want to know what it is

Search tags, topics

#Object.P #Deep #Path

Add a RequireOnlyOne object type

๐Ÿฉ Feature Request

Describe the solution you'd like

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?

Help with Path* methods

๐Ÿค” Question

Describe your question

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?

strange behavior with PathValid

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?

Correct type of currying

๐Ÿค” Question

Describe your question

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]
  ])
)

v3.0.0 testing

I've pushed a pre-release npm i ts-toolbelt@next --save with the following changes:

Breaking changes:

  • Removed N.Clamp
  • All types returning 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:

  • Increased computation speed
  • Removed excess extends
  • Integrated Boolean internally
  • Fixed buggy type distribution

I'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)

Patch version is not incremental

๐Ÿž Bug Report

Describe the bug

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:

  • 3.8.70
  • 3.8.1567788970295
  • 3.8.1567788972460
  • 3.8.69

70 < 1567788970295 < 1567788972460 > 69

Here is the Semantic Versioning Spec.

Could you help unpublish the version numbers that are not incremented?

[4.9.0] dropping type distribution / union handling by default

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 unions every time, but then I was left with a few doubts:

  • This is complex and messes up the codebase
      X extends unknown ? do something with X : never
  • This could interfere with the way we deal with members

    Should types like At be able to operate on unions, or not, how?

  • Could definitely cause performance issues
  • Types distribute with extends & mapped
  • What if the user did not want to do this?

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 unions. Read below to see how.

This leaves unaffected:

  • All Union, U utilities

This affects types like:

  • Merge
  • Pick
  • Omit
  • ...Keys to keep keyof-like behaviors

From 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

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.

Try implement recursive type fellow your way, but not work

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.

No index.js file so eslint complaining it cannot resolve the import

๐Ÿž Bug Report

Describe the bug

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.

Expected behavior

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.

extract keys of type (key type, not its field type)

๐Ÿฉ Feature Request

Describe the solution you'd like

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?

Way to turn properties into X | null?

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 }

Broken unions with recursive types

๐Ÿค” Question

Describe your question

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.

Search tags, topics

#broken #union #recursive

How to use this module?

๐Ÿค” Question

How to use this module? Is it possible used with ramda?

Describe your question

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

Type inference not working with F.Curry and generic function

๐Ÿค” Question

Describe your question

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?

Search tags, topics

#typescript #curry #toolbelt #generic #function #parametrized #type #inference #not #working

I can't import anything

๐Ÿž Bug Report

Describe the bug

The exports are empty. Does not matter if I use import { O } ... or import tb ...

image

happens in both [email protected] and [email protected]

Reproduce the bug

npx create-react-app tb-test --typescript
cd tb-test
yarn add ts-toolbelt
code .

Expected behavior

Possible Solution

image
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.

Screenshots

Additional context

Examples in docs

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.

๐Ÿฉ Feature Request: add examples to docs

Is your feature request related to a problem? Please describe

The docs have everything necessary, except for examples.

Describe the solution you'd like

Each function/type should have at least one example of how it can be used. Lodash and Ramda docs get it right, for example.

Describe alternatives you've considered

None. Examples are necessary to help people understand what each function/type/utility does in a clear manner. They are indispensable.

Teachability, Documentation, Adoption, Migration Strategy

Examples:

Merge of Union

๐Ÿค” Question

Describe your question

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;
	}

Search tags, topics

merge unions

How to write the type of an async pipe function?

๐Ÿค” Question

Describe your question

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.

Search tags, topics

#promise, #async

[4.9.0] clearer documentation

The clutter has been removed out of the documentation site. It previously had mixed internals as well as the exposed API, which was confusing.

add type for callback

๐Ÿฉ Feature Request

Describe the solution you'd like

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.