typescript - TypeScript 中的泛型函数:“T”可以用与“X”无关的任意类型实例化
问题描述
我有一些 TypeScript 代码,我试图通过为函数引入泛型类型来概括,但 TypeScript 给了我一个我无法完全理解的错误消息。
我已将我的代码缩减为以下示例代码,其中添加到示例中的注释有望显示我的意图:
/**
* The types in this example
*/
// A "container" object, which has folders array,
// which is of either FolderString or FolderNumber.
interface FolderContainer<T extends FolderString | FolderNumber> {
folders: T[]
}
// Bear with me; the kind of two silly types, just for the sake of the example:
interface FolderString extends FolderContainer<FolderString> {
age: string
}
interface FolderNumber extends FolderContainer<FolderNumber> {
age: number
}
/**
* Utility function for mapping each folder in the data structure,
* but keep the structure (eg. you cannot map each folder to another type)
*
* I wrote T as T extends {folders: T[]}, as I believe this is enough to describe
* what the shape of T is in order for the fn to work.
*
* I did not think I should set T to for example
* T extends FolderString | FolderNumber as that is, as far as I can understand,
* irrelevant for the mapFoldersKeepStructure fn.
*/
export function mapFoldersKeepStructure<T extends {folders: T[]}>(
folders: T[],
mapper: (folder: T, parents: readonly T[]) => T,
parents: readonly T[] = []
): T[] {
return folders.map((folder) => {
const f = mapFoldersKeepStructure(folder.folders, mapper, [...parents, folder])
return mapper({...folder, folders: f}, parents)
})
}
/**
* Two objects which has the shape of the FolderContainer, where it's folders is of the
* type given
*/
const stringFolders: FolderContainer<FolderString> = {folders: [{age: '13', folders: []}]}
const numberFolders: FolderContainer<FolderNumber> = {folders: [{age: 13, folders: []}]}
// First example of mapFoldersKeepStructure, which works as I expect.
// mapReturn is FolderString[]
const mapReturn = mapFoldersKeepStructure(stringFolders.folders, (folder) => {
return folder
})
// Second example of usage, now wrapped in someFunction()
function someFunction(folders: FolderNumber[]): FolderNumber[] {
return mapFoldersKeepStructure(folders, (folder) => {
return folder
})
}
// Second example works and returns type as I expect.
// someFunctionReturn is FolderNumber[]
const someFunctionReturn = someFunction(numberFolders.folders)
/**
* I want to make the previous someFunction() generic.
* I thought my first tiny step could be to substitute FolderNumber type with a generic type.
*
* I though this change would not do much special, but obviously this is where my
* current understanding of TS and generics starts to break down, as TS responds with:
*
* Type '{ folders: T[]; }[]' is not assignable to type 'T[]'.
* Type '{ folders: T[]; }' is not assignable to type 'T'.
* 'T' could be instantiated with an arbitrary type which could be unrelated to '{ folders: T[]; }'.ts(2322)
*/
function someFunctionGeneric<T extends FolderNumber>(folders: T[]): T[] {
return mapFoldersKeepStructure(folders, (folder) => {
return folder
})
}
如果您能引导我了解或解释我缺少哪个难题来理解这一点,那将有很大帮助:-)
解决方案
推荐阅读
- sql - 将数据类型更改为浮点数并舍入为 2 位小数
- javascript - 使用 jQuery 选择所有至少有 2 个子元素的元素
- php - 如何登录网站并保存 cookie?
- javascript - 新日期时使用附加对象更新关键 playerEffectiveDetails
- ibm-cloud-private - 有没有办法移动已安装 ICP 的 ICP 私有映像注册表的位置?
- html - 输入中的上标
- python - 如何只向用户询问一次符号值而不是在每次迭代中?
- c# - 图像未显示在 datagridview 中
- python - 提高在列表中查找项目索引的速度
- windows - WebPack devServer - localhost 拒绝连接