P Varga
P Varga

Reputation: 20259

Significance of "extends {}"

In existing code, I've come across type parameter declarations like this: <Param extends {}>.

My question is, what is the meaning of the extends {} part? How does it differ from simply <Param>?


Example:

type Fold<S extends {}, D extends {}> = {
  folder: ...
}

Upvotes: 2

Views: 1892

Answers (1)

ford04
ford04

Reputation: 74620

{} vs unknown constraint

If you write <S>, S is unconstrained - it behaves like <S extends unknown> (TS 3.5+).

{} type can take every value (incl. primitives), except null or undefined with strictNullChecks.

unknown is more or less {} | null | undefined, making {} a more narrow subtype:

type FoldImplicit<S> = S
type FoldObject<S extends {}> = S

type T1 = FoldImplicit<"foo"> // ✅ 
type T2 = FoldImplicit<undefined> // ✅
type T3 = FoldObject<"foo"> // ✅
type T4 = FoldObject<undefined> // ❌ - `undefined` not in `{}` (strictNullChecks)
// compiles with `strictNullChecks` disabled 
// - null and undefined now belong to every type.

Where do I see these constructs?

The most often encountered case is probably with React generic components:

// help compiler to distinguish JSX tag from generic component declaration
const Comp = <T extends {}>(props: { foo: T }) => <div>...</div>

To match implicit constraints more closely, you can write <T extends unknown> or just <T,> (note the comma). Before TS 3.5, the implicit constraint has been {}.

With extends {}, you can also take away undefined or null as possible inputs. But in my opinion a) it makes code unnecessarily verbose b) you better find a more narrow constraint for stronger types.

Playground sample

Upvotes: 5

Related Questions