Typescript中的强类型转换

时间:2020-03-02 16:36:48

标签: typescript

如果强制转换为接口的对象类型没有接口定义的原型/结构,有什么办法得到错误?

interface IUser {
 id : string,
 name : string,
 address : string
}

仅填充IUser的几个属性

let data : any = {}; //let data : IUser = <IUser>{}; 
data.id = "123";
data.name = "name";

预期下面的编译时错误。

const user : IUser = <IUser>data; //<= Any way to get an error here saying address property doesn't exist at compile time?

更新:添加为何存在这种要求的上下文。

考虑IUser接口具有许多属性,并且在构造它时不要错过分配任何属性。在这种情况下,只有编译错误会有所帮助。这主要用于在DDD设计中传递DTO对象。这些通常带有许多参数。

2 个答案:

答案 0 :(得分:2)

嗯,这取决于。

对于初学者:作为编译器的TypeScript编译器不能在运行时检查接口 。它可以检查代码中的类型。

因此,如果您只有一些来自外部来源的随机数据,则TypeScript编译器在运行时将无法为您做任何事情:有问题的数据将在编译器执行任务很长时间后生成。为此,您需要一些运行时检查系统,例如io-ts

如果您的需求更加有限,则可以编写自定义type guard。诸如此类(前面未经验证的代码):

function isIUser(obj: any): obj is IUser {
    return obj && typeof obj.id === 'string' && typeof obj.name === 'string' && typeof obj.address === 'string';
}

let data : any = {}; //let data : IUser = <IUser>{}; 
data.id = "123";
data.name = "name";
if (isIUser(data)) {
  // inside this block data is of type IUser
} else {
  // error
}

第二:TypeScript强制转换实际上不执行任何操作或检查。这是一种告诉TS编译器“相信我,我比你更了解的地方,这确实是IUser”。
而且,作为听话的软件,编译器相信您通过的所有内容:好的数据,坏的数据,字符串,数字,未定义的,您自己命名的。因此,请将您的演员人数减少到最低。

如果您允许编译器执行工作,编译器将为您彻底检查:

// Do NOT use any as a type. Tell the compiler what he should enforce
const data : IUser = {
  id: "123",
  name: "name"
}; // --> compile time error.

编辑:

如果您需要调用过程或其他程序,则只需使用支持变量:

// telling the compiler the result must honor the IUser interface
mapIUserDto(userInputs: Something): IUser {
  const name = this.normalizeName(userInputs.name),
        address = this.mapAddress(userInputs);

  // using object composition you colud skip even property names
  // if a property is missing, you'll get a nice error message. 
  return {
    id: userInputs.id,
    name: name,
    address: address
  };
}

答案 1 :(得分:1)

接口具有许多属性,并且在构造它时不要错过分配任何属性

您可以使用构建器以类型化的方式向对象增量添加属性:

function build<T extends object, K extends string, V>(
    o: T, name: K, val: V): asserts o is T & { [P in K]: V } {
    (o as any)[name] = val // add property
    // safe cast, we just fulfill function's type contract
}
let data = {};

build(data, "id", "123")
data // { id: string; }

build(data, "name", "Lui")
data // { id: string; } & { name: string; }

let user: IUser = data // error: address is missing

build(data, "address", "Buenos Aires")
user = data // data is now assignable to IUser

build的行为类似于变量分配,并且对传入的data进行突变。它利用assertion function通过控制流将每个新属性添加到data的类型。

Playground


说明

build接收三个参数:对象onameval,因此可以向o添加属性。现在,有趣的部分是返回类型asserts o is T & { [P in K]: V }

让我们首先解释T & { [P in K]: V }:这意味着,您将传入新添加的属性 plus 的类型为o的任何对象T的类型名称为K,值为V& { [P in K]: V })。

asserts o与类型保护类似,但是后者没有使用两个条件分支(if/else),而是断言一个特定类型的值。通常,当不满足条件时,您将引发一个断言函数。在这里,我们只是利用其通过control flow在周围范围内缩小函数arg o: T的能力。 注意:断言函数无法返回任何内容。

有效地,您会得到一个不错的增量生成器。