Reputation: 20259
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
Reputation: 74620
{}
vs unknown
constraintIf 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.
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.
Upvotes: 5