gitbeaker icon indicating copy to clipboard operation
gitbeaker copied to clipboard

Types is incorrect after omitting keys

Open nilennoct opened this issue 3 years ago • 2 comments

Description

After omitting some fields from existing Schema types, the types of remained fields become unknown.

Steps to reproduce

const api = new Gitlab({ token: 'token' });
const branch = await api.Branches.show('projectId', 'master');
const commitDiff = await api.Commits.diff('projectId', branch.commit.id);
// TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'.

Expected behaviour

The type of branch.commit.id should be string.

Actual behaviour

The type of branch.commit.id is unknown.

Possible fixes

All Schema types extend from Record<string, unknown>. For example:

interface Foo extends Record<string, unknown> {
    x: string;
    y: number;
    z: boolean;
}

type K0 = keyof Foo; // string | number
type K1 = Exclude<K0, 'x'>; // string | number
type T = Omit<Foo, 'x'>; // { [x: string]: unknown, [x: number]: unknown }

While the other is not extended from Record type:

interface Bar {
    x: string;
    y: number;
    z: boolean;
}

type K0 = keyof Bar; // 'x' | 'y' | 'z'
type K1 = Exclude<K0, 'x'>; // 'y' | 'z'
type T = Omit<Bar, 'x'>; // { y: number, z: boolean }

Because Omit is implemented by Exclude, so the excluded keys is incorrect if the object type extends from Record type.

nilennoct avatar Oct 08 '21 13:10 nilennoct

https://github.com/microsoft/TypeScript/issues/36981 Looks like a larger problem

jdalrymple avatar Oct 08 '21 19:10 jdalrymple

Seems that this issue has been rescheduled. The related PR resolve the problem by writing Omit with remapping (microsoft/TypeScript#42524). However, key remapping via as was introduced since TypeScript 4.1, users who use an old version of TypeScript will get syntax error.

nilennoct avatar Oct 09 '21 03:10 nilennoct

@jdalrymple Sorry for the ping, but probably it won't be fixed any time soon.

As a workaround, I suggest using a utility type instead of Omit. Exept provided by type-fest works fine it seems.

I prepared an example with BranchSchema:

export interface BranchSchema extends Record<string, unknown> {
    ...
    commit: Omit<CommitSchema, 'web_url' | 'created_at'>;
}

Direct usage of CommitSchema works just fine:

let date: Date | undefined;
let title: string;

const commit: CommitSchema = {} as any;

title = commit.title;       // no errors
date = commit.created_at;   // no errors

However, for BranchSchema it shows an error:

const branch: BranchSchema = {} as any;

title = branch.commit.title;      // Type 'unknown' is not assignable to type 'string'.(2322)
date = branch.commit.created_at;  // Type 'unknown' is not assignable to type 'Date | undefined'.(2322)

In another example I replaced Omit with Except and now only expected error is shown:

export interface BranchSchema extends Record<string, unknown> {
    ...
    commit: Except<CommitSchema, 'web_url' | 'created_at'>;
}

const branch: BranchSchema = {} as any;

title = branch.commit.title;      // no errors
date = branch.commit.created_at;  // Type 'unknown' is not assignable to type 'Date | undefined'.(2322)

WiseBird avatar Oct 14 '22 16:10 WiseBird

Good find! I keep this in mind to make the tweak when i have a moment!

jdalrymple avatar Oct 17 '22 23:10 jdalrymple

@jdalrymple Is it fixed? I still see Omit usages in the types.

WiseBird avatar Jun 06 '23 18:06 WiseBird

Now that i review this, it looks like i didn't fully solve this previously. I will now though! Should have it back up in an hour or so

jdalrymple avatar Jun 06 '23 18:06 jdalrymple

I see that you are in Montreal, I should definitely buy you a beer someday

WiseBird avatar Jun 06 '23 18:06 WiseBird

Gonna try this type: https://github.com/microsoft/TypeScript/issues/54451(MappedOmit), looks alot stricter than Omit, tell me if you run into any problems once i deploy!

jdalrymple avatar Jun 06 '23 18:06 jdalrymple

:rocket: Issue was released in 38.12.0 :rocket:

jdalrymple avatar Jun 06 '23 21:06 jdalrymple