I have a union type of objects that can under certain conditions be assigned a special behavior. This behavior depends on the type of object. Some of the objects are more special than others and can have more or different functions.
I want to create a factory function that takes an instance of one item of this union type and returns the right behavior class for this instance. It would look like this:
type A = {
type: "A"
name: string,
}
type B = {
type: "B",
name: string,
age: number
}
type Things = A | B
type Base<T extends Things> = {
f1(): string
}
type Special<T extends Things> = T extends B ? { f2(): number } : {}
type Behavior<T extends Things> = Base<T> & Special<T>
class BehaviorClassA implements Behavior<A> {
constructor(private readonly instance: A) { }
f1(): string {
return "some A behavior for " + this.instance.name
}
}
class BehaviorClassB implements Behavior<B> {
constructor(private readonly instance: B) { }
f1(): string {
return "some B behavior for " + this.instance.name
}
f2(): number {
return this.instance.age + 42
}
}
function getBehaviorForThing<T extends Things>(thing: T): T extends B ? BehaviorClassB : BehaviorClassA {
if (isA(thing)) {
return new BehaviorClassA(thing) // Type 'BehaviorClassA' is not assignable to type 'T extends B ? BehaviorClassB : BehaviorClassA'.
}
if (isB(thing)) {
return new BehaviorClassB(thing) // Type 'BehaviorClassB' is not assignable to type 'T extends B ? BehaviorClassB : BehaviorClassA'.
}
throw new Error("invalid thing")
}
function isA(t: Things): t is A {
return t.type === "A"
}
function isB(t: Things): t is B {
return t.type === "B"
}
// consumer:
const myB: B = {
type: "B",
name: "John",
age: 23
}
getBehaviorForThing(myB).f2()
However as you can see the factory function can not be typed correctly.
Does anyone have an idea what I would have to do to get it work as intended?
Function overload is a correct option, but you can do the followinf as well which is a "kind of" type guard:
type ReturnType<T> = T extends B? BehaviorClassB : BehaviorClassA
function getBehaviorForThing<T extends Things>(thing: T): ReturnType<T> {
if (isA(thing)) {
const resu: BehaviorClassA = new BehaviorClassA(thing)
return resu as ReturnType<T>
}
if (isB(thing)) {
return new BehaviorClassB(thing) as ReturnType<T>
}
throw new Error("invalid thing")
}
// consumer:
const myB: B = {
type: "B",
name: "John",
age: 23
}
getBehaviorForThing(myB).f2() <-- is type BehaviorClassB
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.