typescript - TypeScript:强制转换整个类类型
问题描述
我想知道是否可以更改类属性的类型?但不是简单的强制转换,而是覆盖整个类类型。
假设我有以下课程:
class Test {
public myNumber = 7;
}
是否可以将myNumber属性的类型从number更改为string?
假设我们有一个自定义的 typescript 转换器,它将类的每个 number 类型的属性转换为字符串。那么在开发过程中以某种方式反映这一点会很酷。这就是为什么我问是否可以调整类型。
我正在寻找一个选项来覆盖整个类类型定义而不强制转换每个属性。例如不这样做:
const test = new Test();
(
test.myNumber as string).toUpperCase();
我的第一个想法是这可以通过索引类型来实现。但我想问是否有人已经有这方面的经验或有具体的想法。
例如,调用一个函数会很酷......
whatever(Test)
...在此之后,类的类型发生了变化。所以编译器应该知道从现在开始,例如myNumber应该是string而不是number类型。
所以现在应该可以了:
const test = new Test();
test.myNumber.toUpperCase();
这个例子的意义并不重要。这只是一个虚构的用例,以(希望)简单的方式说明问题。
===
因为在这个问题的评论中提到了上下文(如何使用该类),所以我想另外提供以下示例。它是使用jasmine和karma的Angular组件的测试(规范)文件。我试图用代码片段中的注释来解释自己。
describe('ParentComponent', () => {
let component: ParentComponent;
let fixture: ComponentFixture<ParentComponent>;
/**
* This function is the "marker" for the custom typescript transformer.
* With this line the typescript transformer "knows" that it has to adjust the class ParentComponent.
*
* I don't know if this is possible but it would be cool if after the function "ttransformer" the type for ParentComponent would be adjusted.
* With adjusted I mean that e.g. each class property of type number is now reflected as type string (as explained in the issue description above)
*/
ttransformer(ParentComponent);
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ParentComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ParentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('test it', () => {
// This cast should not be necessary
(component.ttransformerTest as jasmine.Spy).and.returnValue('Transformer Test');
expect(component.ttransformerTest).toHaveBeenCalled();
});
});
===
在此先感谢,卢卡斯
解决方案
这是一种奇怪的模式,而不是我建议轻而易举的事情,但它在很大程度上是可以实现的:
实例和构造函数当你声明你的 classTest
时,Typescript 确实做了两件事:一个名为的类型Test
,它是一个Test
类的实例,一个名为的值Test
,它是一个构建该类型的构造函数Test
。虽然 TS 能够给它们赋予相同的名称,并通过一个class
声明将它们带入范围,但我们必须单独处理它们。
因此,处理实例时,我们可以编写一个类型,将Test
带有数字的实例转换为带有字符串的实例:
type NumbersToStrings<T> = {
[K in keyof T]: T[K] extends number ? string : T[K]
}
type TransformedTest = NumersToStrings<Test>; // Remember, `Test` is the instance not the constructor
转换构造函数
现在我们需要表示一个构建这些TransformedTest
实例的构造函数。我们可以手动编写一个带有与构造函数匹配的参数的构造函数Test
:
type TransformedTestCtor = new(/*constructor arguments*/) => TransformedTest;
或者我们可以编写一个类型,它接受一个构造函数并返回一个接受相同 arg 但构造不同实例的构造函数:
type ClassTransform<
OldClass extends new (...args: any[]) => any,
NewType
> = OldClass extends new (...args: infer Args) => any
? new (...args: Args) => NewType
: never;
// Test - as a value is the constructor, so `typeof Test` is the type of the constructor
type TransformedTestCtor = ClassTransform<typeof Test, TransformedTest>;
用法
所以现在我们有一个构造函数,它接受相同的参数但返回一个不同的实例。我们如何实际使用它?
不幸的是,这样的语法不起作用:
whatever(Test)
您通常可以使用asserts
签名更改函数参数的类型,但它不适用于类。
所以,没有比仅仅断言类型更好的方法了:
const TransformedTest = Test as unknown as TransformedTestConstructor;
通过将此构造函数命名为与我们之前定义的实例类型相同,我们模仿了构造函数(值)和实例(类型)共享相同名称的常见模式。(并且可以,例如一起导出)
另一种选择是将其放入返回转换后类型的函数中:
function transformType(Type: typeof Test) {
return Test as unknown as TransformedTestConstructor;
}
const TransformedTest = transformType(Test);
这将Test
作为转换后的构造函数返回:但它不会TransformedTest
像普通类一样将类型带入范围 - 它只会将构造函数(作为值)带入范围。所以如果Test
是可变的并且你这样做:
Test = transformType(Test);
然后值Test 将是新的构造函数,但类型仍将是旧实例。
推荐阅读
- java - SpelEvaluationException: EL1010E: 不能在“RequestControlContextImpl”类型的对象上设置属性或字段“messageContext”
- c++ - 使用opencv的imread时如何确定图像格式/编码
- android - hyperlog-android 并非所有日志都发送到服务器。如何解决这个问题?
- excel - Excel Conditional Formatting - Highlight row with the next date from TODAY
- python - scipy.stats 是否为不同的计算机硬件产生不同的随机数?
- python - Python:为什么我会收到 AttributeError:__enter__
- java - 将按钮添加到带有条件的 javafx 表视图
- sql - “sp_updatestats”
- azure - AAD - 请求中指定的回复 url 与子域请求不匹配
- ios - NSPredicate 逐字匹配字符串