TypeScript 条件类型和计算对象属性名称

Posted

技术标签:

【中文标题】TypeScript 条件类型和计算对象属性名称【英文标题】:TypeScript conditional type and computed object property names 【发布时间】:2021-07-07 17:05:17 【问题描述】:

我在将条件类型与计算对象属性名称结合使用时遇到问题。基本上我是根据输入字符串将行插入到数据库中。然后我根据该输入字符串键入返回对象。返回对象中的属性之一是计算名称,该名称也基于输入字符串。因此,打字稿似乎拥有验证这是否正确所需的所有信息,但它不断给我错误。这是一个非常简化的示例。


//types of the table rows from the database
interface FirstTableRow 
    id: number,
    someFirstRefId: number
;
interface SecondTableRow 
    id: number,
    someSecondRefId: number
;

//maps which table we're working with to its reference column name
const whichToExtraColumn = 
    first: 'someFirstRefId',
    second: 'someSecondRefId'
 as const;

//maps the table to the returned row type
type ConstToObj<T> = (T extends 'first'
    ? FirstTableRow
    : T extends 'second'
    ? SecondTableRow
    : never
);

function createFirstOrSecond<
    T extends keyof typeof whichToExtraColumn
>(
    which: T
): ConstToObj<T> 

    //gets the reference column name for this table
    const refColumn = whichToExtraColumn[which];

    //do database stuff....
    const insertId = 1;

    //build the inserted row
    const test: ConstToObj<T> = 
        id: insertId,
        [refColumn]: 123
    ;
    // ^ Type ' [x: string]: number; id: number; ' is not assignable to type 'ConstToObj<T>'

    return test;

;

我通过对refColumn 进行if-check 来解决此问题,然后根据它生成不同的对象。但是使用计算属性名称会更容易。任何帮助将不胜感激。

【问题讨论】:

我认为 Typescript 编译器运行的问题是 "first" | "second"keyof typeof whichToExtraColumn 的有效子类型,所以 ConstToObj&lt;T&gt; 可能是 FirstTableRow | SecondTableRow 并且对象不能分配给它.不幸的是,错误消息很短,所以很难说到底为什么会出现这种不匹配(以上是猜测) @JonasWilms,嗯,好点。关于如何更改函数以使用条件的任何想法?我已经尝试了很多,但似乎无法正常工作。 【参考方案1】:

您在这里遇到了多个问题:

(1) 计算属性名称被加宽,有人可能会说this is a bug:

type Key = "a" | "b";
let a: Key = Math.random() ? "a" : "b";
const result =  [a]: 1 ;
//    ->  [x: string]: number 

因此,您的示例 [refColumn]: 123 永远不会按照您的意愿行事。

(2) 具有泛型参数的函数的函数体不会使用所有可能的子类型进行迭代验证(我猜编译器可能会永远运行),而是使用类型约束验证它们。因此,如果您有两种泛型类型,而一种是从另一种派生的,Typescript 根本不在乎。通常这不是问题,因为通常一种类型直接是另一种的子类型:

function assign<A extends B, B extends 1 | 2 | 3>(a: A) 
    const b: B = a;

您创建了一个并非如此的案例,约束检查将始终失败。

(3) 不能分配给延迟条件类型。 Typescript 不知道条件类型将采用哪个分支(如果它的评估被延迟),因此只能将 any 分配给它。

function plusOne<A extends 1 | 2>(a: A) 
    const b: (A extends 1 ? 2 : 3) = a + 1;

因此,有了这三个限制,基本上不可能在没有手动类型转换的情况下编写函数。这是as any 似乎非常合理的少数情况之一。

【讨论】:

好吧,这一切都向我解释了。感谢您的回答!

以上是关于TypeScript 条件类型和计算对象属性名称的主要内容,如果未能解决你的问题,请参考以下文章

Typescript:至少有一个T类型属性的对象。

typescript 计算属性允许您使用在运行时动态计算的名称在对象上定义属性。

在 TypeScript 和/或 JSDoc 中,如何指示记录类型中的某些属性名称是同一类型中兄弟属性的别名?

TypeScript:在 keyof T 处获取属性的类型

TypeScript:推断嵌套联合类型的类型

Typescript杂谈