I want to use the is operator to narrow a generic type to either a "union of primitive types" or simply the object type. Either way would work for my use case.
What's the proper workaround for the following snippet? (playground)
class Foo<T = any> {
constructor(value: (() => T) | (T extends Function ? never : T)) {}
}
// Function values must be returned by a getter:
new Foo(() => () => {}) // OK: Inferred as "Foo<() => void>(value: () => () => void)"
// All other values can be optionally wrapped with a getter, or passed as-is:
new Foo(() => 1) // OK: Inferred as "Foo<1>(value: 1 | (() => 1))"
new Foo(1) // OK: Inferred as "Foo<1>(value: 1 | (() => 1))"
// Try narrowing to a primitive type:
function testPrimitive<T>(value: T) {
if (isPrimitive(value)) {
return new Foo(value) // ERROR: "Type 'T & string' is not assignable to type 'T & symbol extends Function ? never : T & symbol'."
}
}
// Try narrowing to an object type:
function testObject<T>(value: T) {
if (isObject(value)) {
return new Foo(value) // ERROR: "Type 'T & object' is not assignable to type 'T & object extends Function ? never : T & object'."
}
}
type Primitive = string | number | boolean | symbol | null | undefined
const isPrimitive = (value: any): value is Primitive =>
!value || typeof value !== 'object'
const isObject = (value: any): value is object =>
value && typeof value === 'object'