Ts-toolbelt: Purpose and safety of Any/Compute

Created on 6 Nov 2020  路  1Comment  路  Source: millsp/ts-toolbelt

I'm looking at the Any/Compute type and my most immediate reaction is: "It seems super useful, so probably TS has a good reason not to compute the final type by default".

However, I think that I lack the deep TS expertise to know why that's the case. With that in mind:

  • Is Any/Compute safe to use for every type? Are there known edge cases?
  • Is it known why TypeScript chooses not to compute the final type by default? Is that just for efficiency?

Thanks for a great library! 馃檪

enhancement question

Most helpful comment

I assume that this is purely for efficiency yes, or so that we can abstract certain types away (that we don't want to show), or both at the same time.

It is completely safe, there is absolutely no type loss. However, there is a drawback at the moment:

type O = {
    a: 'a'
    o: O
} & {
    b: {
        o: O
    }
}

type ComputedO = Compute<O>

Will yield a type that, when you look at it, is filled with anys. It just looks like any. In fact the type information is well preserved:

type test0 = ComputedO['o']['o']['o']['a']

This happens only on circular references. Lucky for us, I'm going to roll out a version that is able to handle circular references - to have even more beautiful outputs. If you want, give it a shot:

import {M} from 'ts-toolbelt'

export type ComputeDeep<A extends any, Seen = never> =
    A extends object
    ? A extends M.BuiltInObject | Seen
      ? A
      : {
            [K in keyof A]: ComputeDeep<A[K], A | Seen>
        } & {}
    : A

type A = {
    a: string
    o: O
}

type O = {
    a: 'a'
    o: A
}

type ComputedO = ComputeDeep<O>

type test0 = ComputedO['o']['o']['o']['a']

The TypeScript team provide very generic tools to do many things with the type system, they give us the choice to build libraries - which is great. But this can make it hard to learn and his is what ts-toolbelt tries to solve.

Thanks for a great library!

Thanks! If you have any question, I'm always here to help!

>All comments

I assume that this is purely for efficiency yes, or so that we can abstract certain types away (that we don't want to show), or both at the same time.

It is completely safe, there is absolutely no type loss. However, there is a drawback at the moment:

type O = {
    a: 'a'
    o: O
} & {
    b: {
        o: O
    }
}

type ComputedO = Compute<O>

Will yield a type that, when you look at it, is filled with anys. It just looks like any. In fact the type information is well preserved:

type test0 = ComputedO['o']['o']['o']['a']

This happens only on circular references. Lucky for us, I'm going to roll out a version that is able to handle circular references - to have even more beautiful outputs. If you want, give it a shot:

import {M} from 'ts-toolbelt'

export type ComputeDeep<A extends any, Seen = never> =
    A extends object
    ? A extends M.BuiltInObject | Seen
      ? A
      : {
            [K in keyof A]: ComputeDeep<A[K], A | Seen>
        } & {}
    : A

type A = {
    a: string
    o: O
}

type O = {
    a: 'a'
    o: A
}

type ComputedO = ComputeDeep<O>

type test0 = ComputedO['o']['o']['o']['a']

The TypeScript team provide very generic tools to do many things with the type system, they give us the choice to build libraries - which is great. But this can make it hard to learn and his is what ts-toolbelt tries to solve.

Thanks for a great library!

Thanks! If you have any question, I'm always here to help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

martaver picture martaver  路  5Comments

ctrlplusb picture ctrlplusb  路  6Comments

tonivj5 picture tonivj5  路  3Comments

ctrlplusb picture ctrlplusb  路  5Comments

millsp picture millsp  路  3Comments