Typescript 方法 return 基于属性的类型
Typescript method return type based on attributes
Typescript 的条件 : T extends U ? X : Y
语法非常强大,但我还没有找到一种方法来根据 class 的属性指定 return 的类型,或者函数本身。例如,采用这个简单的 class:
class X {
members: number[] = [];
returnRoman = false;
first(): number|string {
if (!this.returnRoman) {
return members[0];
} else {
return numberToRomanNumeral(members[0]);
}
}
}
有没有办法将类型 returned 缩小为简单的数字或字符串以进行类型检查?我遇到的特殊问题是我有这样的结构:
class Base {
x: number = 0;
}
class Sub extends Base {
y: number = 0;
}
class FilterIterator {
members: Base[] = [];
filterClass: typeof Base = undefined;
class first(): Base {
for (const m of this.members) {
if (this.filterClass !== undefined && !(m instanceof this.filterClass)) {
continue;
}
return m;
}
}
}
const fi = FilterIterator();
fi.filterClass = Sub
fi.members = [new Sub(), new Sub()];
const s = fi.first();
if (s.y > 0) {
console.log('s is moving up in the world!');
}
因为 fi.first()
被键入 return Base
,行 if (s.y > 0)
引发错误,因为 y
在 Base 上不存在。在这种情况下,做起来并不难:
const s = fi.first() as Sub;
但如果我们有这样的东西:
class FilterIterator extends Base {
...
filterClass = FilterIterator; // I know, not possible simply, but there are ways...
}
然后你得到的代码如下:
const fi = FilterIterator();
[ set up .members with nested FilterIterators ]
const firstOfFirstOfFirst = fi.first().first().first();
被重写为:
const firstOfFirstOfFirst = (((fi.first() as FilterIterator).first()
as FilterIterator).first() as FilterIterator);
如果我们实际创建可以 return 生成器等的生成器,情况会变得更糟,因为在 for...of
循环中声明类型尚不可能。它还将确定值 returned 应该是什么的逻辑移动到消费软件,其中 FilterIterator 或 Base 本身应该知道 classes 可能是什么 returned.
欢迎任何解决方案或改进。
您的简化示例更难正确键入,因为 returnRoman
是一个实例变量,可以假定在同一实例中多次更改其值。您必须通过 setter 更新它并使用语法 asserts this is this & SomeType
.
您的实际用例可能会 运行 遇到同样的困难,需要在类型已经设置后进行转换,但我们可以通过将 filterClass
作为参数来规避这些问题在构造函数中。这也意味着始终可以为实例正确推断 filterClass
的类型,因为从创建实例的那一刻起就知道了。所以它成为 generic classes.
的直接用例
type Constructor<T> = new (...args: any[]) => T;
class FilterIterator<T> {
members: (T | Base)[] = [];
private filterClass: Constructor<T>;
constructor( filterClass: Constructor<T> ) {
this.filterClass = filterClass;
}
first(): T {
for (const m of this.members) {
if (m instanceof this.filterClass) {
return m;
}
}
// you need to either throw an error or expand your return type to include undefined
throw new Error("no match found");
}
}
instanceof
是打字稿中的 automatic type guard ,所以如果你用 return m
打字稿知道 m
保证是相同的类型 T
你的 filterClass
.
我认为您正在尝试做一些不同的事情,其中过滤器是某种自定义检查而不是 class 构造函数,但我没有足够的信息来真正正确地键入它。您可以将过滤器定义为 user-defined type guard.
type Filter<T> = (value: any) => value is T;
class CustomFilterIterator<T> {
members: any[] = [];
private filter: Filter<T>;
constructor( filter: Filter<T> ) {
this.filter = filter;
}
first(): T | undefined {
for (const m of this.members) {
if (this.filter(m)) {
return m;
}
}
return;
}
}
const isNumber = (value: any): value is number => {
return typeof value === "number"
}
const numberFilter = new CustomFilterIterator(isNumber);
numberFilter.members = [55, "55", "hello", 78.3];
numberFilter.first()?.toExponential();
Typescript 的条件 : T extends U ? X : Y
语法非常强大,但我还没有找到一种方法来根据 class 的属性指定 return 的类型,或者函数本身。例如,采用这个简单的 class:
class X {
members: number[] = [];
returnRoman = false;
first(): number|string {
if (!this.returnRoman) {
return members[0];
} else {
return numberToRomanNumeral(members[0]);
}
}
}
有没有办法将类型 returned 缩小为简单的数字或字符串以进行类型检查?我遇到的特殊问题是我有这样的结构:
class Base {
x: number = 0;
}
class Sub extends Base {
y: number = 0;
}
class FilterIterator {
members: Base[] = [];
filterClass: typeof Base = undefined;
class first(): Base {
for (const m of this.members) {
if (this.filterClass !== undefined && !(m instanceof this.filterClass)) {
continue;
}
return m;
}
}
}
const fi = FilterIterator();
fi.filterClass = Sub
fi.members = [new Sub(), new Sub()];
const s = fi.first();
if (s.y > 0) {
console.log('s is moving up in the world!');
}
因为 fi.first()
被键入 return Base
,行 if (s.y > 0)
引发错误,因为 y
在 Base 上不存在。在这种情况下,做起来并不难:
const s = fi.first() as Sub;
但如果我们有这样的东西:
class FilterIterator extends Base {
...
filterClass = FilterIterator; // I know, not possible simply, but there are ways...
}
然后你得到的代码如下:
const fi = FilterIterator();
[ set up .members with nested FilterIterators ]
const firstOfFirstOfFirst = fi.first().first().first();
被重写为:
const firstOfFirstOfFirst = (((fi.first() as FilterIterator).first()
as FilterIterator).first() as FilterIterator);
如果我们实际创建可以 return 生成器等的生成器,情况会变得更糟,因为在 for...of
循环中声明类型尚不可能。它还将确定值 returned 应该是什么的逻辑移动到消费软件,其中 FilterIterator 或 Base 本身应该知道 classes 可能是什么 returned.
欢迎任何解决方案或改进。
您的简化示例更难正确键入,因为 returnRoman
是一个实例变量,可以假定在同一实例中多次更改其值。您必须通过 setter 更新它并使用语法 asserts this is this & SomeType
.
您的实际用例可能会 运行 遇到同样的困难,需要在类型已经设置后进行转换,但我们可以通过将 filterClass
作为参数来规避这些问题在构造函数中。这也意味着始终可以为实例正确推断 filterClass
的类型,因为从创建实例的那一刻起就知道了。所以它成为 generic classes.
type Constructor<T> = new (...args: any[]) => T;
class FilterIterator<T> {
members: (T | Base)[] = [];
private filterClass: Constructor<T>;
constructor( filterClass: Constructor<T> ) {
this.filterClass = filterClass;
}
first(): T {
for (const m of this.members) {
if (m instanceof this.filterClass) {
return m;
}
}
// you need to either throw an error or expand your return type to include undefined
throw new Error("no match found");
}
}
instanceof
是打字稿中的 automatic type guard ,所以如果你用 return m
打字稿知道 m
保证是相同的类型 T
你的 filterClass
.
我认为您正在尝试做一些不同的事情,其中过滤器是某种自定义检查而不是 class 构造函数,但我没有足够的信息来真正正确地键入它。您可以将过滤器定义为 user-defined type guard.
type Filter<T> = (value: any) => value is T;
class CustomFilterIterator<T> {
members: any[] = [];
private filter: Filter<T>;
constructor( filter: Filter<T> ) {
this.filter = filter;
}
first(): T | undefined {
for (const m of this.members) {
if (this.filter(m)) {
return m;
}
}
return;
}
}
const isNumber = (value: any): value is number => {
return typeof value === "number"
}
const numberFilter = new CustomFilterIterator(isNumber);
numberFilter.members = [55, "55", "hello", 78.3];
numberFilter.first()?.toExponential();