Home > database >  TypeScript Optimization Help - "Type instantiation is excessively deep and possibly infinite.(2
TypeScript Optimization Help - "Type instantiation is excessively deep and possibly infinite.(2

Time:01-05

I have some TypeScript code where I'm trying to extract data- attributes from a full object of element attributes. However, I'm running into an error, "Type instantiation is excessively deep and possibly infinite.(2589)".

I think my code can probably be optimized to fix this—I might be overcomplicating it—so I'd appreciate an expert eye to see if it can be helped, or if what I'm trying to do is too difficult.

Here's a link to the TS Repl, and I've included the code below:

type AnyArray = any[];
type AnyArrayWithItems = [any, ...any];
type AnyFunction<Arguments extends any[] = any[]> = (...args: Arguments) => any;

// The type of the first item in an array.
// Input: `[1, 2, 3]`
// Output: `1`
type Head<SomeArray extends AnyArray> = SomeArray extends AnyArrayWithItems
    ? SomeArray[0]
    : never;

// The type of an array after removing the first element.
// Input: `[1, 2, 3]`
// Output: `[2, 3]`
type Tail<SomeArray extends AnyArray> = AnyFunction<SomeArray> extends (
    _: any,
    ...args: infer Remainder
) => any
    ? Remainder
    : never;

const DATA_KEY_PREFIX = "data-" as const;

type DataKey = `${typeof DATA_KEY_PREFIX}${string}`;

type PrependIfDataKey<
    Key extends string | number | symbol,
> = Key extends DataKey ? [Key] : [];

type DataKeys<
    Keys extends (string | number | symbol)[],
> = Keys['length'] extends 0 ? [] : [
    ...PrependIfDataKey<Head<Keys>>,
    ...DataKeys<Tail<Keys>>
];

type DataProps<Props extends Record<string, unknown>> = Pick<
    Props,
    DataKeys<(keyof Props)[]>
>;

function getDataAttributes<Props extends Record<string, unknown>>(
    props: Props,
): DataProps<Props> {
    return Object.keys(props)
        .filter((propName): propName is DataKey =>
            propName.startsWith(DATA_KEY_PREFIX),
        )
        .reduce(
            (dataProps, propName) => ({ ...dataProps, [propName]: props[propName] }),
            {} as DataProps<Props>,
        );
}


const test = { href: "#", onClick: () => null, 'data-foo': 'bar' };
const attrs = getDataAttributes(test);

CodePudding user response:

Rather than trying to tease apart your recursive types to see if there's a way to make the compiler happier, I think it might be best to step back and write DataProps directly via key remapping:

type DataProps<T> = { [K in keyof T as Extract<K, DataKey>]: T[K] };

Key remapping lets you omit properties if you remap the key to never. And so I'm using the Extract<T, U> utility type to only keep keys K which are assignable to DataKey.

This works as desired on your example code:

const test = { href: "#", onClick: () => null, 'data-foo': 'bar' };
const attrs = getDataAttributes(test);

type Attrs = typeof attrs;
/* type Attrs = {
    "data-foo": string;
} */

Playground link to code

  •  Tags:  
  • Related