Aron
Aron

Reputation: 9248

Why is a generic type that extends an interface not assignable to a matching object?

I am getting this error

[ts] Type '{ type: string; }' is not assignable to type 'A'.

with the below code

interface Action {
    type: string;
}

function requestEntities<A extends Action>(type: string) {
    return function (): A {
        return { type };
    };
}

Why isn't it assignable? A extends Action, which has only one property: type, which is a string. What's the problem here?

Is the problem that A could have more properties? then how do I tell TypeScript that A still only has the type: string property and nothing else?

EDIT

FYI the reason that I want to add the generic A is because A will have a specific string as the type property, e.g. { string: 'FETCH_ITEMS' }.

Upvotes: 2

Views: 3464

Answers (3)

Daniel Khoroshko
Daniel Khoroshko

Reputation: 2721

Look what you can do on order to achieve stronger type safety (I didn't fully understand your task, but the approach should be clear from this example)

interface Action {
    type: string;
    amount: number;
}

const action: Action = { type: 'type1', amount: 123 }

function requestEntities<KEY extends keyof Action>(type: KEY) {
    return action[type]
}

requestEntities('type')
requestEntities('amount')

requestEntities('random-stuff')

Shows error:

Upvotes: 0

Paleo
Paleo

Reputation: 23712

FYI the reason that I want to add the generic A is because A will have a specific string as the type property, e.g. { string: 'FETCH_ITEMS' }.

Because you are sure that A is compatible with Action, you can reassure the compiler:

return { type } as A;

Upvotes: 2

jcalz
jcalz

Reputation: 329418

The generic isn't helping you here. As you note, A can have more properties:

interface SillyAction extends Action {
   sillinessFactor: number;
}
requestEntities<SillyAction>('silliness');

There generally isn't a way in TypeScript to say that an object has only some set of properties, because TypeScript currently lacks exact types.

But in your case, you want the returned Action to have a type with a specific string; something like:

interface SpecificAction<T extends string> extends Action {
   type: T;
}
function requestEntities<T extends string>(type: T) {
    return function (): SpecificAction<T> {
        return { type };
    };
}
requestEntities('silliness'); // returns a function returning {type: 'silliness'}

Hope that helps. Good luck!

Upvotes: 5

Related Questions