Typescript:是否可以向类型联合添加扩展方法,例如T | null | undefined?

Typescript:是否可以向类型联合添加扩展方法,例如T | null | undefined?,typescript,extension-methods,Typescript,Extension Methods,我正在学习typescript语言特性,最近我已经学会了如何为内置类(数组、字符串、对象等)和外部模块编写扩展方法。然而,我仍然无法理解是否可以扩展类型,比如类型联合、交叉和其他东西 例如,有时在调用一些方法后,我会将变量类型化为T | null: 接口书{标题:字符串,作者:作者[]} const books:Book[]=。。。 书。 //一些操作 .find(项=>item) //在这里,我想调用方法 Array.prototype.find方法返回T | null,我想将其转换为动态,

我正在学习typescript语言特性,最近我已经学会了如何为内置类(数组、字符串、对象等)和外部模块编写扩展方法。然而,我仍然无法理解是否可以扩展类型,比如类型联合、交叉和其他东西

例如,有时在调用一些方法后,我会将变量类型化为
T | null

接口书{标题:字符串,作者:作者[]}
const books:Book[]=。。。
书。
//一些操作
.find(项=>item)
//在这里,我想调用方法
Array.prototype.find
方法返回
T | null
,我想将其转换为动态,但这是不可能的。当然,我可以将整个方法链包装成
Optional.ofNullable(/*这里是巨大的代码片段*/)
。那看起来很难看

到目前为止,我所做的是尝试向
对象
接口添加扩展方法,并使用类型注释限制
这个

声明全局{
接口对象{
toOptional(this:T | null):可选;
}
}
//上述方法的实现
书
.find(book=>Boolean(book.title)和&book.authors.length>1)
.toOptional()
.map(book=>book.title)
.ifPresent(console.log);

它不起作用。我希望这段代码能够记录任何有多个作者的标题,但它甚至不会编译。老实说,Typescript不允许在这样的类型联合上使用任何方法。结果:
对象可能为空

Typescript实际上正在保存您的实现,这可能会导致空指针异常。在链中使用t | null是没有意义的

我想你弄错了。用过滤器过滤。找到了吗?既然您将find的结果视为一个数组

const book=[
{title:'这是一个标题',作者:[1]},
{title:'这是另一个标题',作者:[1,2]}
]
书
.filter(book=>Boolean(book.title)和&book.authors.length>1)
.map(book=>book.title)

.forEach(console.log)
本机无法执行此操作。我能想到的唯一解决方法(除了包装整个链)是覆盖
find
方法,以返回
可选的
,而不是
T | null
。但我不建议这样做,因为您会更改整个页面的实现。因此,如果库依赖于
find
返回
T | null
而不是
可选的
,那么它会给您带来问题

您可以创建自己的
Array
实现来缓解该问题,但在将现有阵列转换为实现时会有一些开销

class MyArray<T> extends Array {
  public find(cb:any):Optional<T> {
    return Optional.ofNullable(super.find(cb));
  }
}

interface Book { title: string, authors: Author[] }
const books: MyArray<Book> = new MyArray(); // Create clean instance

const booksNormal: Book[] = [/* Some Values */];
const books2: MyArray<Book> = new MyArray(...booksNormal); // Transform existing, "normal" array

books.
    // some operations
    .find(item => item)
    .map(book => book.title)
    .ifPresent(console.log);

类MyArray扩展了数组{
公共查找(cb:any):可选{
返回可选的.ofNullable(super.find(cb));
}
}
接口书{标题:字符串,作者:作者[]}
const books:MyArray=newmyarray();//创建干净的实例
常量booksNormal:Book[]=[/*某些值*/];
常量books2:MyArray=newmyarray(…booksNormal);//转换现有的“普通”数组
书。
//一些操作
.find(项=>item)
.map(book=>book.title)
.ifPresent(console.log);

有趣的问题。我想在这里分享我对联合类型声明和使用扩展方法的初步想法:

当您使用诸如
string | number
之类的联合类型时,您将只能使用联合中所有类型共享的接口。如果您想对这样的联合类型使用扩展方法,我想您必须在所有这些类型上分别实现扩展方法(具有兼容的签名)。在这种情况下,
字符串
界面上的扩展方法以及
数字
界面上的扩展方法。通过这种方式,您还可以实现应用于这些特定类型的独立逻辑。将自动调用正确的扩展方法

当您的联合中并非所有类型都实现扩展方法时,如
string | null | undefined
,您必须在代码中包含适当的类型保护,以确保您没有对未实现它的类型调用(扩展)方法。否则将出现编译错误。在我看来,这只是默认的类型脚本行为;扩展方法和本机方法之间没有区别

当您希望对泛型类型(如
)调用(扩展)方法时,必须确保这些泛型类型将继承实现这些(扩展)方法的类型。这也是默认的TypeScript行为;在这里,扩展方法和本机方法之间也没有区别

话虽如此,我想说的是,关于扩展方法的使用有一个非常生动的讨论。它们可能与现有的或未来的(本机)实现发生冲突,因此对它们要非常小心

现在,让我们看看您的问题和您提供的示例

嗯,对于示例代码的解决方案,我认为您根本不需要自定义扩展方法。如果您只想显示具有多个作者的所有书籍的标题,可以使用以下代码:

书籍
.filter(book=>book.title&&book.authors.length>1)
.forEach(book=>console.log(book.title));
(请注意,
Array
filter
方法返回一个数组,其中包含满足提供的测试函数的所有元素,但是
find
方法仅返回数组中满足提供的测试函数的第一个元素。)

如果这个简单的解决方案不适合你在生产中的实际场景(也许是因为你的例子被大大简化了),你仍然想要尽可能地使用干净流畅的界面,你可能会考虑FO。