#How to extend type from 3rd party library

15 messages · Page 1 of 1 (latest)

stone ether
#

I'm using 3rd party library (library for communicating with Commerce tools, but that does not matter).
I have a function that returns a Category, but the issue here is that category can have a custom field that is not accounted in the type provided with said library. So when I'm trying to do something like this:

const getCategory = (): Category => {
    return {/* ... */}
}

const myField: string = 'my-field';
const categoryOrginal = getCategory();
const orignalField = categoryOrginal.custom[myField]; // does not work - TS complains

Typescript throws an error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'CustomFieldsType'.
No index signature with a parameter of type 'string' was found on type 'CustomFieldsType'.(7053)

So I have some string variable with a name of my field, but I can not index into the category, because the type does not include such possibility (even though in reallity the object itself has that field).

What I'm trying to do it to provide my own new type, that I could assign to category, in which I could extend the type to include additional fields:

type ExtendedCustomFieldsType = CustomFieldsType & Record<string, string>;

type ExtendedCategory = Omit<Category, 'custom'> & {
    custom?: Maybe<ExtendedCustomFieldsType>;
};

const categoryTest: ExtendedCategory = getCategory(); // error

This way I should be able to index using any string and the error should go away. But it is not the case I have a another weird error.

#

I'm getting this weird error:

Type 'Category' is not assignable to type 'ExtendedCategory'.
Type 'Category' is not assignable to type '{ custom?: Maybe<ExtendedCustomFieldsType> | undefined; }'.
Types of property 'custom' are incompatible.
Type 'Maybe<CustomFieldsType> | undefined' is not assignable to type 'Maybe<ExtendedCustomFieldsType> | undefined'.
Type 'CustomFieldsType' is not assignable to type 'ExtendedCustomFieldsType'.
Type 'CustomFieldsType' is not assignable to type 'Record<string, string>'.
Property 'typeRef' is incompatible with index signature.
Type 'Reference' is not assignable to type 'string'.(2322)

And I'm not sure what is going on. I prepared a reproduction script in the playground here: https://www.typescriptlang.org/play/?ts=4.3.5#code/PTAEGYCcBNQBwIaQC4E9QBsCWAjSTUAuAKDTgFNQBZBVHcgHgBUA+UAXlCdAB9QA7AK4YMAbmKlUFUADVykAM5YA9v3KxOAb2KhdoLNEKgFySFn4BzcXtAA3eUtVGhAW3qRxAX3GTpAJXIAM3lyfgBjSi0dPQB9GLJQhBdyAH4jAHIA4MhQiPTrPQSASUNjU3MrYm8JBK4pcgARIPMsZBV+DlkHdvVQADJQbRs4hP4k1IymeqbAlrbVfOjdAGtyIjKzSwLdMeS0jYqvH1q-BAB3AGFBE2UXADEscgwNQaXQEfrdidBM86ub+6PZ6LGxfIwmTaVGy2BAYQTkcHlLZVY71UD-ZC3B5PaAKKbSKLDeKfcb7dIYrFA3H48ggwr1LJGLIhcLkbagBL7Gh0Rg0mZzdosdlha6YwE4hSnM5c2j0BgAQUg+FQDClFPFzxYQpRNTRFwQyHIFmUkHQnDkih6sAGQ1ixIoXzJ+sNxtNdN0BkRkPZq1Q+whhzeIoBMp5DHV2OeePq2uqxDCqhMoAs5GQzqNJrNoAAFABKIzp11mti23Q5ZCCSAdTSen4GdIAGjs3ScoAAjJ4qhIQKAAOoACwNoCKAnIvUxoGgyhICf4SZcqEjpQDlk66QXAFpZjjFrOk2EDRnTQB5SAWcywzoptOHot58R75CgE1YCxjDBLzoHl2Z0-n98AHTBmKADaC5LgAuqIoA9lO5AKAIyhPmcJrLKAG5cAAyqACYuHAGAIOYCgSMQPZUOgBqGnhT5Dso9hKgYCK+JQACiAAehr8NA6gRlS0YEuioqUhKNL9KAAQJjADArhYTYydqzGgOxnHcdAhaZp0x4uK04a3pmTbpMBtzpGwNpBkJLihnKymhKpvEiTGRwkY+OF6aaTDwcgRg2VxPFuVm17qaa94wWAC6gJR5DUZOyjwYhyGoaA-YhBILmGiYn6cN+R6oB5JhgYuVJQaFEVcRFGAKMoz5nheGCgPIkAmvoCEmFgIgcslORAA

I tried to recreate the chain of all the types, so it is as close as possible to the real world, but I simplified it a bit anyway.

pulsar runeBOT
#

@stone ether Here's a shortened URL of your playground link! You can remove the full link from your message.

_gumaa_#0

Preview:```ts
// 3rd party library:
type Maybe<T> = T | null;

type Versioned = {
id: string;
version: number;
};

type Reference = {
__typename?: 'Reference';
typeId: string;
};

type TypeDefinition = Versioned & {
__typename?: 'TypeDefinition';
key: string;
...```

blazing moat
#

@stone ether Yeah, you aren't going to just be able to assign Category to ExtendedCategory - it's not a safe assignment.

#

You can unsfely cast:

const categoryTest = getCategory() as ExtendedCategory;
#

Or you could try to safely validate it at runtime

stone ether
#

@blazing moat is there a way to like "optionally expand" the type so that they are both interchangeable? I want to avoid doing a hard casting with as

blazing moat
#

If you extend it with optional properties it works;

type ExtendedCategory = Category & {
    myField?: string;
}

const x: ExtendedCategory = getCategory();
#

... but then you'll have to null-check whenever you access myField.

#

You also should probably be changing getCategory to return the extended type, not try to modify the type after calling the function.

#

If CustomFieldsType is something provided by the library, it might be possible to directly modify it.

stone ether
stone ether
blazing moat
#

Specific is generally going to work better and be more type safe.

stone ether
#

Ok, it seems to be working 🤔 Now I need to figure out in my code how I can go from unspecified string to actual values.