Ryan Prentiss
Ryan Prentiss

Reputation: 1642

Vue 3 - How to set Typescript object to one of many interfaces?

Given the following Typescript interfaces, how should I set user to accept interface Baseball, Basketball, or Football, where properties vary?

Currently, the only properties accessible are those which overlap. In this example being user.stats.A.

I am working within Vue 3's Composition API.


interface Profile {
   firstName: string
   lastName: string
   status: string
}

export interface Baseball extends Profile { 
    stats { 
        A: string
        B: string
        ...
    }
}

export interface Basketball extends Profile {
    stats { 
        A: string
        C: string
        ...
    }
}

export interface Football extends Profile {
    stats { 
        A: string
        D: string
        ...
    }
}
setup(){
    const user = reactive<Baseball | Basketball | Football>({
        firstName: 'Michael'
        lastName: 'Jordan'
        status: 'GOAT'
        stats: {
            ...basketball specific properties
        }
    }
})

Upvotes: 0

Views: 2355

Answers (2)

tony19
tony19

Reputation: 138306

A workaround is to move the object initialization outside of the reactive():

import { defineComponent, reactive } from 'vue'

//...

export default defineComponent({
  setup() {
    const initProfile: Baseball | Basketball | Football = {
      firstName: 'Michael',
      lastName: 'Jordan',
      status: 'GOAT',
      stats: {
        A: '1',
        C: '2',
      }
    }
    const user = reactive(initProfile)

    console.log(user.stats.C) // ✅
  }
})

Upvotes: 1

Vincent Menzel
Vincent Menzel

Reputation: 1055

What you have is perfectly fine. In fact you did the right thing. You will always have to provide at least the properties of interface of the union type. I would suggest to do something like this

// I(nterface)User
type IUser = Baseball | Basketball | Football;

When you are unsure about what object your getting the only thing you can do is test for the properties you need.

Here is an explanation of a custom XOR type. Maybe this can help you. This will not allow you to put A, B AND C but only the properties of a single interface.

type Without < T, U > = {
  [P in Exclude < keyof T, keyof U > ] ? : never
};
type XOR < T, U > = (T | U) extends object ? (Without < T, U > & U) | (Without < U, T > & T) : T | U;

interface Profile {
  firstName: string
  lastName: string
  status: string
  stats: XOR < XOR < Baseball, Basketball > , Football >
}

export interface Baseball {
  A: string
  B: string
}

export interface Basketball {
  A: string
  C: string
}

export interface Football {
  A: string
  D: string
}



type IUser = Profile

const user: IUser = {
  firstName: 'Michael',
  lastName: 'Jordan',
  status: 'GOAT',
  stats: {
    A: '',
    B: ''
  }
};

Upvotes: 2

Related Questions