void } interface H2 { "click": () => void } type A = H1 extends Han" /> void } interface H2 { "click": () => void } type A = H1 extends Han" /> void } interface H2 { "click": () => void } type A = H1 extends Han"/>

What is the difference between type and interface in Conditional Types?

381 views Asked by At
type Handlers = {
    [name: string | number]: () => void
}

type H1 = { 
    "click": () => void 
}

interface H2 {
    "click": () => void
}

type A = H1 extends Handlers ? 'yes' : 'no' // yes
type B = H2 extends Handlers ? 'yes' : 'no' // no

tsc v5.1.6.

Although H1 and H2 have the same structure, I obtained that A is 'yes' and B is 'no'.

It seems that there are some differences between type and interface in the extends clause. I have read the typescript manual twice, but I cannot find any relevant explanations.

I don't know if it is related to some of the underlying logic of TSC?

Is the type checking of the Interface stricter than that of the Type?

2

There are 2 answers

0
Aman Khan On

type can represent a wide range of types, including primitive types, unions, intersections, etc.

interface is more focused on object shape definition and can be extended by other interfaces.

type works as expected in conditional types and the conditional type checks if the specified type is assignable to another type.

interface in conditional types behaves differently. TypeScript's conditional types use structural compatibility for checks, and interfaces are open-ended.

H1 extends Handlers because it matches the structure exactly, so type A = 'yes'. H2 doesn't extend Handlers in a conditional type context due to the difference in behavior between type and interface, so type B = 'no'.

Example:-

Type:-

type H1 = { 
    "click": () => void 
}
type A = H1 extends Handlers ? 'yes' : 'no' // yes

Interface:-

interface H2 {
    "click": () => void
}
type B = H2 extends Handlers ? 'yes' : 'no' // no

interface H2Extended extends H2 {
    "hover": () => void
}

let h2Var: H2;
let h2ExtVar: H2Extended;

h2Var = h2ExtVar; // This is valid
0
stax On

In TypeScript, both type and interface can define object shapes, but they have some subtle differences. In your example, the issue arises because of how they interact with the extends clause.

When you use a type with extends, TypeScript checks if the type being examined (left side) fits the constraints of the type it's compared to (right side). In your case, H1 extends Handlers because Handlers allows any string or number as keys, and H1 has a matching property "click".

However, with an interface like H2, even though it has the same structure as H1, TypeScript doesn't see it as extending Handlers. This behavior stems from internal TypeScript rules that make the two behave slightly differently in certain situations.

In simple terms, type and interface both define object shapes, but when dealing with extends and mapped types, their behavior might differ due to how TypeScript internally evaluates them. It's important to choose the right one based on what you need in your code.