javascript - 为什么我不能将 typescript 泛型类型分配给该类型的联合?

标签 javascript typescript typeerror typescript-generics

我的一个网络应用程序中有以下类型:

import React, {FC} from 'react';
import { Table, Tbody, Td, Th, Thead, Tr, chakra } from '@chakra-ui/react';
import { TriangleDownIcon, TriangleUpIcon } from '@chakra-ui/icons'
import {useTable, useSortBy, Column} from 'react-table'
import {Adult, Child} from "~/src/types/models";

type PersonTablePropData =
    | {
        persons: Adult[]
        columns: readonly Column<Adult>[]
      }
    | {
        persons: Child[]
        columns: readonly Column<Child>[]
      }

export type PersonTableProps = PersonTablePropData & {
    showPerson: (personId: number) => void
}

const PersonTable: FC<PersonTableProps> = ({ persons, showPerson, columns }) => {

  const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
    useTable<Adult|Child>({ columns, data: persons }, useSortBy)
...

这会导致最后一行的“列”出现错误:

Type 'readonly Column<Adult>[] | readonly Column<Child>[]' is not assignable to type 'readonly Column<Adult | Child>[]'.

这让我有点不知所措:不应该 readonly Column<Adult>[]可以分配给 readonly Column<Adult | Child>[] ?据我所知,这将是一个直接子集,那么这里出了什么问题?

最佳答案

This leaves me at a bit of a loss: Shouldn't readonly Column[] be assiganble to readonly Column<Adult | Child>[]? As far as I can see, it would be a straight subset, so what is going wrong here?

你说得完全正确。正常情况下应该是可以赋值的。

type Adult = {
  tag: 'Adult',
  name: string,
  age: number
}

type Child = {
  tag: 'Child',
  school: number
}

let adult: Adult[] = []
let both: Array<Child | Adult> = []

both = adult // ok,
adult = both // error, as expected

但是,对于函数参数来说,情况并非如此。剧透:逆变。

let adultFn = (arg: Adult[]) => { }
let bothFn = (arg: Array<Child | Adult>) => { }

bothFn = adultFn // error
adultFn = bothFn // ok

adult不再可分配给 bothFn 。继承的方式向相反的方向改变。

让我们让它更接近您的示例:


type Adult = {
  tag: 'Adult',
  name: string,
  age: number
}

type Child = {
  tag: 'Child',
  school: number
}

type Options<T> = {
  columns: T[],
  handler: (arg: T) => void
}

let adult: Adult[] | Child[] = []

let hook = <T,>(arg: Options<T>) => { }

// same error as you have
hook<Adult | Child>({ columns: adult })

正如您可能已经注意到的,此错误与您的错误相同。现在,尝试删除 handler: (arg: T) => void来自Options<T>类型。错误就会消失。为什么 ?因为逆变。这将使您的代码更加安全。 useTable钩子(Hook)在引擎盖下的用途UseTableOptions类型,如果提供通用 D用于逆势位置 - 争论位置。

type UseTableOptions<D extends object> = {
    columns: ReadonlyArray<Column<D>>;
    data: readonly D[];
} & Partial<{
    initialState: Partial<TableState<D>>;
    stateReducer: (newState: TableState<D>, action: ActionType, previousState: TableState<D>, instance?: TableInstance<D>) => TableState<D>;
    useControlledState: (state: TableState<D>, meta: Meta<D>) => TableState<D>;
    defaultColumn: Partial<Column<D>>;
    getSubRows: (originalRow: D, relativeIndex: number) => D[];
    getRowId: (originalRow: D, relativeIndex: number, parent?: Row<D>) => string;
    autoResetHiddenColumns: boolean;
}>

请参阅另一个简化示例:

type Options<T> = {
  handler: (arg: T) => void
}

type SuperType = string;
type SubType = 'hello'

declare let superType: SuperType;
declare let subType: SubType;

superType = subType // ok
subType = superType // error


let superTypeObj: Options<SuperType> = {
  handler: (arg) => { }
}


let subTypeObj: Options<SubType> = {
  handler: (arg) => { }
}

// opposite direction
superTypeObj = subTypeObj // error
subTypeObj = superTypeObj // ok

Playground

有关 *-variance 的更多信息,您可以找到 herehere

关于javascript - 为什么我不能将 typescript 泛型类型分配给该类型的联合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72575150/

相关文章:

typescript - Typescript 中错误的自动返回类型推导

python - 日期操作类型错误(日期时间包)

javascript - 如何将 jQuery 代码(更改类和文本的单击事件)转换为 JavaScript

javascript - 如何使用 vue-test-utils 和 jest 在单元测试期间模拟 Vue Mixins?

javascript - 当使用服务器而不是本地计算机时,我应该用什么替换 "http://localhost:3000"?

javascript - 使用 typescript 和 babel 时重新映射 Istanbul 尔

javascript - JS 到 PHP 的 POST 请求 : wrong JSON

javascript - 导入自定义 NPM 库时无法在模块外使用导入语句

python - 类型错误: '<' 和 'NoneType' 实例之间不支持 'str'

javascript - 在 React nodeJS 中使用 mysql