英文:
Is it possible to modify a Typescript type alias dynamically?
问题
我正在开发一个 Angular 库,它将作为一个 API 客户端。我遇到的问题是,一些使用该库的应用程序正在使用 HttpInterceptor 来自动将日期字符串转换为 JavaScript 日期对象,而其他一些应用程序则只使用响应中传递的字符串值。
我目前将这些属性类型定义为 string | Date
,但我真的希望根据消费库的应用程序来缩小联合类型,使其要么是 string
要么是 Date
。
我首先尝试声明一个类型别名 type DateType = string | Date
,然后希望能够在消费应用程序中使用 declare global
块来覆盖它,但这并不起作用。
我还考虑过定义一个接口 interface DateType extends String
,然后在消费应用程序中覆盖它,但我放弃了这条路,因为我不希望消费应用程序必须管理 String 对象和基本类型之间的转换。
英文:
I am working on an Angular library that will serve as an API client. The issue I'm running into is that some of the consuming applications are using an HttpInterceptor to automatically convert date strings into Javascript Date objects while others just use the string value passed in the response.
I currently have these properties typed as string | Date
but I would really like a way to have the union type narrowed to either string
or Date
based on which application is consuming the library.
I first tried declaring a type alias type DateType = string | Date
and hoped I could override it using a declare global
block in the consuming application but that didn't work.
I also considered defining an interface interface DateType extends String
and then overriding that in the consuming application but I abandoned that route as I don't want to have the consuming applications have to manage the conversion between the String object and primitive.
答案1
得分: 1
为什么不像这样用一个泛型参数来定义你的函数?
declare function callApi<T extends Date | string>(): T;
const date = callApi<Date>();
const str = callApi<string>();
[TypeScript playground][1]
英文:
Why not just type your function with a generic parameter like this?
declare function callApi<T extends Date | string>(): T;
const date = callApi<Date>();
const str = callApi<string>();
答案2
得分: 0
TypeScript不支持对现有类型进行任意修改。特别是你不能修改类型别名。但是TypeScript通过声明合并支持对某些类型进行修改,你可以使用(或滥用)它来获得你所期望的行为。
基本上,你可以“重新打开”接口声明并添加属性。如果库的类型定义如下所示:
interface DateTypeConfig {
[x: string]: string | Date;
}
type DateType = DateTypeConfig["type"];
declare function acceptDate(date: DateType): void;
declare function produceDate(): DateType;
declare function modifyDate(date: DateType): DateType;
那么DateType
是对DateTypeConfig
接口的type
属性的索引访问。现在DateTypeConfig
接口目前没有具体的type
属性,但它有一个类型为string | Date
的string
索引签名,因此默认情况下,DateType
是string | Date
。
因此,默认情况下,消费应用程序将看到这个联合类型。
const d = produceDate();
// const d: string | Date
const m = modifyDate(d);
// const m: string | Date
acceptDate(m);
但是消费应用程序可以重新打开DateTypeConfig
接口并添加一个明确的type
属性,其类型是string | Date
的某个子类型。 (如果这是在模块中,或者你的库在一个模块中,你可能需要使用global
扩充或module
扩充来获取正确的命名空间。)
例如:
interface DateTypeConfig {
type: Date;
}
然后自动地,DateType
类型别名变成了Date
,而不是string | Date
:
const d = produceDate();
// const d: Date
const m = modifyDate(d);
// const m: Date
acceptDate(m);
或者应用程序可以这样做:
interface DateTypeConfig {
type: string;
}
然后DateType
将是string
:
const d = produceDate();
// const d: string
const m = modifyDate(d);
// const m: string
acceptDate(m);
英文:
TypeScript doesn't support arbitrary modifications to existing types. In particular you can't modify type aliases. But TypeScript does support certain type modifications through declaration merging, which you can use (or abuse) to get the behavior you're looking for.
Essentially you can "re-open" an interface declaration and add properties to it. If the library's types are defined like this:
interface DateTypeConfig {
[x: string]: string | Date
}
type DateType = DateTypeConfig["type"]
declare function acceptDate(date: DateType): void;
declare function produceDate(): DateType;
declare function modifyDate(date: DateType): DateType;
Then DateType
is an indexed access into the type
property of the DateTypeConfig
interface. Now the DateTypeConfig
interface doesn't currently have a specific type
property, but it does have a string
index signature of type string | Date
, so to start with, DateType
is string | Date
.
So by default, a consuming app would see this union.
const d = produceDate();
// const d: string | Date
const m = modifyDate(d);
// const m: string | Date
acceptDate(m);
But the consuming app could re-open the DateTypeConfig
interface and add an explicit type
property whose type is some subtype of string | Date
. (If this is in a module or your library is in a module you might need to use global
augmentation or module
augmentation to get the right namespace.)
For example:
interface DateTypeConfig {
type: Date;
}
And then automatically, the DateType
type alias becomes Date
instead of string | Date
:
const d = produceDate();
// const d: Date
const m = modifyDate(d);
// const m: Date
acceptDate(m);
Or instead the app could do this:
interface DateTypeConfig {
type: string;
}
and then DateType
would be string
:
const d = produceDate();
// const d: string
const m = modifyDate(d);
// const m: string
acceptDate(m);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论