简体   繁体   中英

TypeScript: Difference Generic Constraints VS Parameter Type

I do not understand the difference between Generic Constraints and plain Parameter Types. In the first case, I do understand that I cannot call si.g() because I strictly typed si to be of type SomeInterface. However, in the second example S extends SomeInterface and from my explanation , extending means it needs to have f() but can contain more properties. But the error is the same. Why?

interface SomeInterface {
    f();
}

function test1(si: SomeInterface) {
    si.f()
    // si.g() -> Property 'g' does not exist on type 'SomeInterface'
}

function test2<S extends SomeInterface>(si: S) {
    si.f()
    // si.g() -> Property 'g' does not exist on type 'SomeInterface'
}

Typescript needs to check that you're passing a type that includes the function g . So you need to do something like this:

interface ExtendedInterface extends SomeInterface {
 g();
}

...

function test2<E extends ExtendedInterface>(ei: E) {
    si.f()
    si.g()
}

Of course, you can also do this:

function test3(ei: ExtendedInterface) {
    ei.g();
    ei.f();
}

And that'll be functionally identical to the version with generics.

So what's the difference? Nothing here, but generics give you more power at the expense of more complex code. For example, generics allow you to declare a type parameter that is constrained by another type parameter.

...extending means it needs to have f() but can contain more properties. But the error is the same. Why?

Because it may or may not have more properties, and if it does, they may have any name or any type. TypeScript doesn't know what those properties are, so inside test2 , TypeScript only knows that si is a SomeInterface .

You opened by saying you didn't understand the difference between type parameters ( <S extends SomeInterface> and si: S ) vs. just putting types on parameters ( si: SomeInterface ). Type parameters let you do more interesting things, like:

function pluck<Type extends object, Key extends keyof Type>(array: Type[], key: Key) {
    return array.map(element => element[key]);
}

const objects = [{a: 2, b: "s"}, {a: 3, b: "x"}];
const x = pluck(objects, "a"); // `x` is `number[]`
const y = pluck(objects, "b"); // `y` is `string[]`

Playground link

I could be mistaken, but I don't think you can do that without type parameters (or type assertions).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM