打字稿-逐步扩展对象的类型

时间:2019-12-06 16:23:31

标签: typescript typescript-generics

我正在尝试通过TS实现以下目标:

let m: Extendable
m.add('one', 1)
// m now has '.one' field
m.add('two', 2)
// 'm' now has '.one' and '.two' fields

我熟悉通过以下方式在TS中返回扩展类型:

function extend<T, V>(obj: T, val: V): T & {extra: V} {
    return {
        ...obj,
        extra: val
    }
}

现在,我的情况有两个问题:

1)对象m在调用add()后需要更新其类型,以反映新字段的添加

2)参数化了新字段的名称(例如,并非总是extra

第一个问题可能是通过使用类定义并以某种方式使用TypeThis实用程序来重新调整类型来解决的,但是我找不到足够的文档来使用它。

欢迎任何帮助或指导。谢谢!

1 个答案:

答案 0 :(得分:4)

TypeScript 3.7引入了assertion functions,可用于缩小传入参数甚至this的类型。断言函数看起来像user-defined type guards,但是您在类型谓词之前添加了asserts修饰符。这是将Extendable作为声明方法的类来实现add()的方法:

class Extendable {
    add<K extends PropertyKey, V>(key: K, val: V): asserts this is Record<K, V> {
        (this as unknown as Record<K, V>)[key] = val;
    }
}

当您调用m.add(key, val)时,编译器断言m将具有一个属性,其键的类型为key,而对应的值的类型为val。使用方法如下:

const m: Extendable = new Extendable();
//     ~~~~~~~~~~~~ <-- important annotation here!
m.add('one', 1)
m.add('two', 2)

console.log(m.one.toFixed(2)); // 1.00
console.log(m.two.toExponential(2)); // 2.00e+0

一切正常。调用m.add('one', 1)后,可以引用m.one,而不会出现编译器警告。

不幸的是,有一个fairly major caveat;断言函数只有在具有显式注释的类型时才起作用。根据{{​​3}},“存在此特定规则,因此对潜在断言调用的控制流分析不会循环触发进一步的分析。”

这意味着以下错误:

const oops = new Extendable(); // no annotation
  oops.add("a", 123); // error!
//~~~~~~~~ <-- Assertions require every name in the call target to be declared with
// an explicit type annotation.

唯一的区别是,{em}推断为oops的类型为Extendable,而不是Extendablem的{​​em {1}}是。而且,您在调用oops.add()时遇到错误。根据您的用例,这可能没什么大不了的,也可能不是一件大事。


好的,希望能有所帮助;祝你好运!

relevant pull request