Tom*_*s F 21 javascript angularjs typescript
使用typescript时,声明的接口可能如下所示:
interface MyInterface {
test: string;
}
Run Code Online (Sandbox Code Playgroud)
具有额外属性的实现可能是这样的:
class MyTest implements MyInterface {
test: string;
newTest: string;
}
Run Code Online (Sandbox Code Playgroud)
示例(此处变量'reduced'仍包含属性'newTest'):
var test: MyTest = {test: "hello", newTest: "world"}
var reduced: MyInterface = test; // something clever is needed
Run Code Online (Sandbox Code Playgroud)
题
一般来说,如何使'reduced'变量只包含'MyInterface'接口中声明的属性.
为什么
尝试在将angular.toJson发送到休息服务之前使用'reduced'变量时会出现问题 - toJson方法会转换newTest变量,即使它在编译期间无法在实例上访问,这也使得其余服务不会接受json,因为它具有不应存在的属性.
这是不可能的。原因interface是 Typescript 构造并且转译的 JS 代码是空的
//this code transpiles to empty!
interface MyInterface {
test: string;
}
Run Code Online (Sandbox Code Playgroud)
因此,在运行时没有什么可“使用”的——不存在可供查询的属性。
@jamesmoey 的回答解释了实现预期结果的解决方法。我使用的类似解决方案只是将“接口”定义为一个类 -
class MyInterface {
test: string = undefined;
}
Run Code Online (Sandbox Code Playgroud)
然后您可以使用lodash从“接口”中选择属性以注入您的对象:
import _ from 'lodash'; //npm i lodash
const before = { test: "hello", newTest: "world"};
let reduced = new MyInterface();
_.assign(reduced , _.pick(before, _.keys(reduced)));
console.log('reduced', reduced)//contains only 'test' property
Run Code Online (Sandbox Code Playgroud)
这是一个务实的解决方案,它很好地为我服务,而不会陷入关于它是否实际上是接口和/或命名约定(例如IMyInterface 或MyInterface)的语义上,并允许您模拟和单元测试
TS 2.1 具有 Object Spread 和 Rest,因此现在可以:
var my: MyTest = {test: "hello", newTest: "world"}
var { test, ...reduced } = my;
Run Code Online (Sandbox Code Playgroud)
之后,reduced 将包含除“test”之外的所有属性。
另一种可能的方法:
正如其他答案所提到的,你无法避免在运行时做一些事情;TypeScript 编译为 JavaScript,主要是通过简单地删除接口/类型定义、注释和断言来实现。类型系统被删除,并且MyInterface在需要它的运行时代码中找不到您的类型系统。
因此,您将需要一些类似要保留在简化对象中的键数组之类的东西:
const myTestKeys = ["test"] as const;
Run Code Online (Sandbox Code Playgroud)
这本身很脆弱,因为如果MyInterface被修改,您的代码可能不会注意到。让代码引起注意的一种可能方法是设置一些类型别名定义,如果myTestKeys与以下内容不匹配,则会导致编译器错误keyof MyInterface:
// the following line will error if myTestKeys has entries not in keyof MyInterface:
type ExtraTestKeysWarning<T extends never =
Exclude<typeof myTestKeys[number], keyof MyInterface>> = void;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type 'UNION_OF_EXTRA_KEY_NAMES_HERE' does not satisfy the constraint 'never'
// the following line will error if myTestKeys is missing entries from keyof MyInterface:
type MissingTestKeysWarning<T extends never =
Exclude<keyof MyInterface, typeof myTestKeys[number]>> = void;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type 'UNION_OF_MISSING_KEY_NAMES_HERE' does not satisfy the constraint 'never'
Run Code Online (Sandbox Code Playgroud)
这不是很漂亮,但是如果您进行更改MyInterface,上面的一行或两行将给出一个错误,希望该错误具有足够的表现力,以便开发人员可以修改myTestKeys。
有多种方法可以使其更加通用,或者可能减少侵入性,但几乎无论您做什么,您可以从 TypeScript 中合理期望的最好结果是,您的代码将在接口发生意外更改时向编译器发出警告;并不是说您的代码实际上会在运行时执行不同的操作。
一旦你拥有了你关心的键,你就可以编写一个pick()函数来从对象中提取这些属性:
function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> {
return keys.reduce((o, k) => (o[k] = obj[k], o), {} as Pick<T, K>);
}
Run Code Online (Sandbox Code Playgroud)
我们可以在您的对象上使用它们来test获取reduced:
var test: MyTest = { test: "hello", newTest: "world" }
const reduced: MyInterface = pick(test, ...myTestKeys);
console.log(JSON.stringify(reduced)); // {"test": "hello"}
Run Code Online (Sandbox Code Playgroud)
这样可行!
| 归档时间: |
|
| 查看次数: |
4875 次 |
| 最近记录: |