Issue
I'm learning TypeScript and I want to extend a type
with an interface
, I've found a blog post which suggests that it's not possible for a type
to extend/implement interfaces
.
Here is a code example of what I would like to do:
interface IPet {
needsFood: boolean
}
// How I would like to use the IPet interface with a type,
type Cat implements IPet = {
needsFood: boolean,
color: string,
}
The above code exampel gives an error. 'IPet' only refers to a type, but is being used as a value here.
Here is how I would use the IPet interface with a Class
, but is it possible to have the same behavior with a type
class Cat implements IPet {
needsFood: boolean;
color: string
}
Is there any way I can have a type
implementing an interface? Or do I need to use a Class
if I want to use interfaces?
Edit: Thanks for all the comments guys, I have updated the question with a code example. What I'm really looking for here is basically just a yes/no answer whether it's possible to have a TypeScript type extend/implement an interface.
Solution
The answer to the question as asked is no, you cannot do this directly with a type
alias. An implements
clause only applies to class
declarations, and an extends
clause only applies to interface
or class
declarations.
Of course it is easy enough to get analogous functionality:
An implements
clause on a class just checks to see if the class instance is assignable to the declared type, and produces a compiler warning if it's not; it has no effect on the type of
the instance. You can do the same thing with a utility type like
type Implements<T, U extends T> = U
So Implements<T, U>
doesn't have an effect on the resulting type, U
, but since U
is constrained to T
, you will get a warning if it is not assignable.
Let's test it:
type Cat = Implements<IPet, {
needsFood: boolean,
color: string,
}> // okay
type Dog = Implements<IPet, {
bark(): void
}>; // error! Property 'needsFood' is missing in type '{ bark(): void; }'
Looks good.
Meanwhile an extends
clause can be used to add members to or narrow members of a parent type. This is essentially the same as an intersection type, and so we can make a utility type to capture this:
type Extends<T, U extends Pick<T, keyof T & keyof U>> =
T & U;
Here we combine both the parent type T
and the new members U
. I've constrained U
to make sure that you can't add conflicting properties, which is similar to what happens if you try to extend an interface with conflicting properties:
type Cat2 = Extends<IPet, {
color: string
}>
type Dog2 = Extends<IPet, {
needsFood: number // error!
}>; // error!
// Type '{ needsFood: number; }' does not satisfy the constraint Pick<IPet, "needsFood">.
Also looks good.
Those approaches aren't completely identical to implements
/extends
clauses, but they should be close enough for a lot of use cases. There are almost surely edge cases where they differ and it's possible these could be repaired or worked around. But the basic takeaway here is that you can use generic constraints and intersection types to build a solution.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.