Reputation: 1642
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
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
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