Reputation: 6431
I would like to have conditional type that something is an object.
type Id = {
id: number
obj: {
x: 5
}
}
type ObjString<T> = {
[P in keyof T]:
T[P] extends Object ? string : T[P]
}
const f: ObjString<Id> = {
id: 4,
obj: "xxxx"
}
here, the obj
property is correctly mapped to string in the f
object, but for the id
property, I'll get this error message:
error TS2322: Type 'number' is not assignable to type 'string'.
That means that T[P] extends Object
is evaluated to true for number
. How should I construct the condition, so number will evaluate to false, whereas object would evaluate to true?
Upvotes: 1
Views: 1733
Reputation: 330216
The Object
type does not really correspond to non-primitives in TypeScript; rather it is the type of a values which can be indexed into like an object. This includes primitives like string
and number
, which get wrapped with objects when you index into them (and thus supports things like "hello".toUpperCase()
and (123).toFixed(2)
). Only null
and undefined
are not Object
s in this sense. Object
in TypeScript is rarely what you want.
If you are trying to find a type in TypeScript which means "non-primitive", you can use the object
type instead (starts with a lowercase o
instead of an uppercase O
):
type ObjString<T> = {
[P in keyof T]:
T[P] extends object ? string : T[P]
}
type Z = ObjString<Id>;
/* type Z = {
id: number;
obj: string;
} */
const f: ObjString<Id> = {
id: 4,
obj: "xxxx"
}
And then everything works how you want here.
Keep in mind though that arrays and functions are also object
s, so you might get some undesirable behavior depending on what you wanted to see there:
type Hmm = ObjString<{ foo(): string, bar: number[] }>;
// type Hmm = { foo: string; bar: string; }
Upvotes: 2
Reputation: 33101
This is because Object
is too general type. Every primitive value in javascript extends Object
because even numbers and string have their own methods.
Consider this example:
type Check1 = string extends Object ? true : false // true
type Check2 = number extends Object ? true : false // true
I think it is better to approach the problem from other side. You can check whether type is primitive or not:
type Id = {
id: number
obj: {
x: 5
}
}
type Primitives = string | number | boolean | bigint | symbol;
type ObjString<T> = {
[P in keyof T]:
T[P] extends Primitives ? T[P] : string
}
const f: ObjString<Id> = {
id: 4,
obj: "xxxx"
}
Upvotes: 1