Lets say I have 3 types. typeA, typeB, and typeC
typeA has a name property while typeB and typeC does not
when I render this with a code like;
interface propTypes {
post: typeA | typeB | typeC
}
..... some react code ....
return(
{post?.name && component})
But it returns an error: "Property 'name' does not exist on type 'typeA| typeB | typeC
I tried post instanceof typeA but it returns an error 'typeA' only refers to a type, but is being used as a value here.
CodePudding user response:
Consider this example:
type typeA = {
name: string
}
type typeB = {
surname: string
}
type typeC = {
age: number
}
type post = typeA | typeB | typeC
interface propTypes {
post: post
}
const App = ({ post }: propTypes) => {
post // no autocomplete
return null
}
There is no autocomplete after post.
because you don't have any common props.
See docs
If we have a value that is a union type, we can only access members that are common to all types in the union.
In order to make it work, you should either define a typeguard:
const isTypeA = (props: unknown): props is typeA => Object.prototype.hasOwnProperty.call(props, 'name')
const App = ({ post }: propTypes) => {
if (isTypeA(post)) {
post.name // string
}
return null
}
OR, you can use in
operator, it works very nice with unions:
const App = ({ post }: propTypes) => {
if ('name' in post) {
post.name // string
}
return null
}
OR you can use StrictUnion
type:
type typeA = {
name: string
}
type typeB = {
surname: string
}
type typeC = {
age: number
}
// credits goes to https://stackoverflow.com/questions/65805600/type-union-not-checking-for-excess-properties#answer-65805753
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
type Post = StrictUnion<typeA | typeB | typeC>
interface propTypes {
post: Post
}
const App = ({ post }: propTypes) => {
if (post.name) {
post.name // string
}
return null
}
the 'in' operator worked for me Please keep in mind that it has own drawbacks:
type typeA = {
name?: string
}
interface propTypes {
post: typeA
}
const App = ({ post }: propTypes) => {
if ('name' in post) {
post.name.charAt // error
}
return null
}
It works perfectly fine with unions, but not with optional properties.