0
votes

I have this code:

export interface StoreCursor {
    created: Date;
    id: number;
}

export default class PageUtil {
    public static decode<T>(cursor?: string, dates?: (keyof T)[]): T | undefined {
        if (!cursor) {
            return;
        }

        const json = Buffer.from(cursor, "base64").toString("utf8");

        return JSON.parse(json, (key, value) => (dates?.includes(key) ? new Date(value) : value));
    }
}

const decoded = PageUtil.decode<StoreCursor>(cursor, ["created"]);

But I get next error, and I don't know how solve it:

error TS2345: Argument of type 'string' is not assignable to parameter of type 'keyof T'.

I've also tried:

public static decode<T>(cursor?: string, dates?: (keyof T & string)[]): T | undefined {
    [...]
}

But the error is similar:

error TS2345: Argument of type 'string' is not assignable to parameter of type 'keyof T & string'. Type 'string' is not assignable to type 'keyof T'.

I want dates to be limited to keys of StoreCursor (or better, only to keys of StoreCursor of type Date).

1
I guess it's complaining correctly, because as your type T you're passing StoreCursor, that doesn't contains any parameter of type string, whereas inside your decode() first param is of type string. So you need to make you Generic Type compatible with your function args. - Mohit
Yeah, but I am trying to refer to the keys of StoreCursor (created or id) not to their values. I also have tried export interface TattooStoreCursor extends Record<string, unknown> { [...] } but the error is the same. - xeladejo

1 Answers

1
votes

The key parameter of the callback to JSON is typed as string, while your array has items of type keyof T. includes expects an element of the same type as the item in the array and there is no way to express in TS something like If it's a literal type or a key type, accept the base type (in this case string).

You have one of two options:

First just assert key is keyof T it might not be, but that is what you are testing so no runtime issue can come from the assertion:

export default class PageUtil {
    public static decode<T>(cursor?: string, dates?: (keyof T)[]): T | undefined {
        if (!cursor) {
            return;
        }

        const json = "";

        return JSON.parse(json, (key, value) => (dates?.includes(key as keyof T) ? new Date(value) : value));
    }
}

Playground Link

Or another option is to use a separate implementation signature in which dates is of type string[]

export default class PageUtil {
    public static decode<T>(cursor?: string, dates?: (keyof T)[]): T | undefined
    public static decode(cursor?: string, dates?: string[]): any {
        if (!cursor) {
            return;
        }

        const json = "";

        return JSON.parse(json, (key, value) => (dates?.includes(key) ? new Date(value) : value));
    }
}

Playground Link