在TypeScript中使用useState定义两种可能的数组类型。

huangapple go评论83阅读模式
英文:

Typing two possible kinds of array in useState with TypeScript

问题

我正在进行常规的电影画廊类型项目,从API下载电影和电视剧并在Row组件中显示它们。现在,这个组件接受一个数组,并将海报映射到用户可以点击并打开的缩略图中。

问题出在TypeScript上,因为useState数组可以接收一组具有特定属性的电影(即名称存储为title属性)或大部分其他属性的电视剧(名称存储为name属性)。

我为这两种类型的对象创建了一个类型,分别称为MovieData和TVData,我在下面使用了它们(isPosterRow定义了是电视剧还是电影):

const [movies, setMovies] = useState<MovieData[] | TVData[]>([]);

然后我这样映射数组:

{movies.map((movie, id) => (
    <CardMedia
        component="img"
        src={`https://image.tmdb.org/t/p/w500/${
            isPosterRow ? movie.poster_path : movie.backdrop_path
        }`}
        alt={isPosterRow ? movie.name : movie.title}
        key={id}
        sx={{
            width: "250px",
            maxHeight: isPosterRow ? "250px" : "100px",
            objectFit: "contain",
            transition: "transform 450ms",
            cursor: "pointer",                    
        }}
    />
))}

在src中,它工作得很好,因为电影和电视剧都包含poster_path和backdrop_path属性。然而,我在CardMedia组件的alt属性中收到了一个错误,属性name在MovieData上不存在,属性title在TVData上不存在(这是正确的)。我该如何解决这个问题?我以为联合类型会在这里起作用,但事实并非如此。

英文:

I'm doing the regular movie gallery type of project and I'm downloading movies and TV series from the API and displaying them in a Row component. Now, this component takes the array and maps the posters into thumbnails users can click and open.

The issue here is with TypeScript, because the useState array can receive an array of movies which have certain attributes (ie names are stored as title attribute) or TV series that have mostly others (names are stored as name attribute).

I've created a type for both types of objects, called MovieData for movies and TVData for Tv series, I've used them as below (isPosterRow defines whether it's TV series or movies):

  const [movies, setMovies] = useState&lt;MovieData[] | TVData[]&gt;([]);

Then I map the array as such:

{movies.map((movie, id) =&gt; (
            &lt;CardMedia
              component=&quot;img&quot;
              src={`https://image.tmdb.org/t/p/w500/${
                isPosterRow ? movie.poster_path : movie.backdrop_path
              }`}
              alt={isPosterRow ? movie.name : movie.title}
              key={id}
              sx={{
                width: &quot;250px&quot;,
                maxHeight: isPosterRow ? &quot;250px&quot; : &quot;100px&quot;,
                objectFit: &quot;contain&quot;,
                transition: &quot;transform 450ms&quot;,
                cursor: &quot;pointer&quot;,                    
              }}
            /&gt;
          ))}

In src it works great because both movies and TV series contain poster_path and backdrop_path attributes. However, I'm getting an error in the alt attribute of the CardMedia component that attribute name doesn't exist on MovieData and title attribute doesn't exist on TVData (which is true). How do I get around this? I thought the union type would work here but it doesn't.

答案1

得分: 3

请尝试以下代码:

interface 电影数据 {
  标题: string;
  海报路径: string;
  // 电影的其他属性...
}

interface 电视数据 {
  名称: string;
  背景路径: string;
  // 电视剧的其他属性...
}

type 电影或电视 = 电影数据 | 电视数据;

function 是否为电影数据(data: 电影或电视): data is 电影数据 {
  return (data as 电影数据).标题 !== undefined;
}

function 是否为电视数据(data: 电影或电视): data is 电视数据 {
  return (data as 电视数据).名称 !== undefined;
}

const [电影列表, 设置电影列表] = useState<电影或电视[]>([]);

{电影列表.map((电影, id) => (
  <CardMedia
    component="img"
    src={`https://image.tmdb.org/t/p/w500/${
      是否为电视数据(电影) ? 电影.背景路径 : 电影.海报路径
    }`}
    alt={是否为电视数据(电影) ? 电影.名称 : 电影.标题}
    key={id}
    sx={{
      width: "250px",
      maxHeight: 是否为电视数据(电影) ? "250px" : "100px",
      objectFit: "contain",
      transition: "transform 450ms",
      cursor: "pointer",
    }}
  />
))}
英文:

Try this

interface MovieData {
  title: string;
  poster_path: string;
  // other attributes for movies...
}

interface TVData {
  name: string;
  backdrop_path: string;
  // other attributes for TV series...
}

type MovieOrTV = MovieData | TVData;

function isMovieData(data: MovieOrTV): data is MovieData {
  return (data as MovieData).title !== undefined;
}

function isTVData(data: MovieOrTV): data is TVData {
  return (data as TVData).name !== undefined;
}

const [movies, setMovies] = useState&lt;MovieOrTV[]&gt;([]);

{movies.map((movie, id) =&gt; (
  &lt;CardMedia
    component=&quot;img&quot;
    src={`https://image.tmdb.org/t/p/w500/${
      isTVData(movie) ? movie.backdrop_path : movie.poster_path
    }`}
    alt={isTVData(movie) ? movie.name : movie.title}
    key={id}
    sx={{
      width: &quot;250px&quot;,
      maxHeight: isTVData(movie) ? &quot;250px&quot; : &quot;100px&quot;,
      objectFit: &quot;contain&quot;,
      transition: &quot;transform 450ms&quot;,
      cursor: &quot;pointer&quot;,
    }}
  /&gt;
))}

huangapple
  • 本文由 发表于 2023年7月20日 20:33:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76729927.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定