【问题标题】:TypeScript empty object for a typed variable类型化变量的 TypeScript 空对象
【发布时间】:2018-01-02 11:47:19
【问题描述】:

说我有:

type User = {
  ...
}

我想创建一个新的user,但将其设置为空对象:

const user: User = {}; // This fails saying property XX is missing
const user: User = {} as any; // This works but I don't want to use any

我该怎么做?我不希望变量为null

【问题讨论】:

  • 要么您希望userUser | {}Partial<User> 类型,要么您需要重新定义User 类型以允许空对象。现在,编译器正确地告诉你user 不是User

标签: typescript


【解决方案1】:

注意事项

这里有两个来自 cmets 的值得注意的警告。

您希望用户的类型为User | {}Partial<User>,或者您需要重新定义User 类型以允许空对象。现在,编译器正确地告诉你用户不是用户。 – jcalz

我认为这不应该被认为是一个正确的答案,因为它创建了一个不一致的类型实例,破坏了 TypeScript 的整个目的。在此示例中,属性Username 未定义,而类型注释表示它不能未定义。 – Ian Liu Rodrigues

回答

design goals of TypeScript 之一是“在正确性和生产力之间取得平衡。” 如果这样做对您有好处,请使用 Type Assertions 为类型变量创建空对象.

type User = {
    Username: string;
    Email: string;
}

const user01 = {} as User;
const user02 = <User>{};

user01.Email = "foo@bar.com";

这里是a working example for you

【讨论】:

  • 谢谢!这样就解决了!这应该被标记为正确答案...
  • 我认为这不应该被认为是一个正确的答案,因为它创建了一个不一致的类型实例,破坏了 TypeScript 的整个目的。在此示例中,属性Username 未定义,而类型注释表示它不能未定义。
  • 好点@IanLiuRodrigues。我在答案中添加了一些警告。
  • @IanLiuRodrigues TypeScript 的目标之一是平衡正确性和生产力,所以说这破坏了 TypeScript 的全部目的有点极端。 github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
  • 也许我的打字稿真的很糟糕,但我发现语言适得其反。我只想做这样的事情(c#)定义一个人:public class Person { int age;然后稍后初始化一个为 null 或仅具有空道具的人: var p = new Person(); // 使用空道具或 Person p = null; // 开始为 null 为什么要这么复杂?
【解决方案2】:

真的取决于你想要做什么。类型是打字稿中的文档,因此您希望在创建类型时表明应该如何使用这个东西的意图。

选项 1:如果用户在其生命周期中可能拥有部分但不是全部属性

使所有属性可选

type User = {
  attr0?: number
  attr1?: string
}

选项 2:如果包含 Users 的变量可能以 null 开头

type User = {
...
}
let u1: User = null;

虽然,实际上,如果这里的重点是在知道将分配给它的内容之前声明 User 对象,那么您可能想要在没有任何分配的情况下执行 let u1:User

选项 3:您可能想要什么

确实,打字稿的前提是确保您符合您在类型中概述的心理模型,以避免出错。如果你想一个一个地向一个对象添加东西,这是 TypeScript 试图让你不要这样做的习惯。

更可能的是,您想要创建一些局部变量,然后在准备好成为完整用户时分配给包含用户的变量。这样,您将永远不会留下部分形成的用户。那些东西太恶心了。

let attr1: number = ...
let attr2: string = ...
let user1: User = {
  attr1: attr1,
  attr2: attr2
}

【讨论】:

  • 不确定这是否是他想要的,但我们可能正在谈论一个名为 UserAttributes 的单独参数或处理传入参数的东西。它可能不是可接受的用户对象,因此可以单独定义。
  • 这不应该是公认的答案。请参阅下面 Shaun Luttin 的帖子。
  • 选项 3 太明显太棒了!谢谢!
  • @thargenediad 为什么?它以语言不打算的方式破坏了类型一致性。如果下游开发人员调用 user01.name,他们将收到 TypeScript 应该防止的运行时错误。这是一个经典的 XY 问题——OP 实际上是在询问如何初始化一个对象,但他们询问的是他们提出的解决方案,而不是询问根本问题。按照 unflores 的建议创建部分对象在这里也可以很好地工作。
【解决方案3】:

请注意,使用 const user = {} as UserType 仅提供智能感知,但在运行时 user 是空对象 {} 并且内部没有属性。这意味着user.Email 将给出undefined 而不是""

type UserType = {
    Username: string;
    Email: string;
}

因此,使用classconstructor 来实际创建具有默认属性的对象。

type UserType = {
  Username: string;
  Email: string;
};

class User implements UserType {
  constructor() {
    this.Username = "";
    this.Email = "";
  }

  Username: string;
  Email: string;
}

const myUser = new User();
console.log(myUser); // output: {Username: "", Email: ""}
console.log("val: "+myUser.Email); // output: ""

您也可以使用interface 代替type

interface UserType {
  Username: string;
  Email: string;
};

...其余代码保持不变。


实际上,您甚至可以跳过constructor 部分并像这样使用它:

class User implements UserType {
      Username = ""; // will be added to new obj
      Email: string; // will not be added
}

const myUser = new User();
console.log(myUser); // output: {Username: ""}

【讨论】:

    【解决方案4】:

    空对象可以写成Record&lt;string,never&gt;,所以实际上你的用户类型要么是空对象要么是用户

    const user : User | Record<string, never> = {};
    

    【讨论】:

      【解决方案5】:

      在我的情况下,Record&lt;string, never&gt; 有帮助,这是 eslint 推荐的

      【讨论】:

        【解决方案6】:

        您可以在 typescript 中按以下方式执行此操作

         const _params = {} as any;
        
         _params.name ='nazeh abel'
        

        因为 typescript 的行为不像 javascript,所以我们必须将类型设置为 any,否则它将不允许您将属性动态分配给对象

        【讨论】:

        • 如果你使用as any,你最好不要使用 TypeScript。永远不要这样做。
        【解决方案7】:
        user: USER
        
        this.user = ({} as USER)
        

        【讨论】:

        • 请为您的答案添加一些解释
        【解决方案8】:

        如果您声明一个空对象字面量,然后稍后分配值,那么您可以认为这些值是可选的(可能存在也可能不存在),因此只需将它们键入为带问号的可选:

        type User = {
            Username?: string;
            Email?: string;
        }
        

        【讨论】:

          【解决方案9】:

          记录

          构造一个对象类型,其属性键为 Keys,其属性值为 Type。此实用程序可用于将一种类型的属性映射到另一种类型。

           const user:Record<string,string>={}
          

          【讨论】:

            【解决方案10】:

            我想要的是intellisense 对中间件链的帮助。 Record&lt;string, never&gt; 为我工作。

            type CtxInitialT = Record<string, never>;
            type Ctx1T = CtxInitialT & {
              name: string;
            };
            type Ctx2T = Ctx1T & {
              token: string;
            };
            
            cont ctx: CtxInitialT = {};
            // ctx.name = ''; // intellisense error Type 'string' is not assignable to type 'never'
            cont ctx1: Ctx1T = middleware1AugmentCtx(ctx);
            // ctx1.name = 'ddd'; // ok
            // ctx1.name1 = ''; // intellisense error Type 'string' is not assignable to type 'never'
            cont ctx2: Ctx2T = middleware2AugmentCtx(ctx1);
            // ctx2.token = 'ttt'; // ok
            // ctx2.name1 = ''; // intellisense error Type 'string' is not assignable to type 'never'
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-10-21
              • 2015-02-05
              • 1970-01-01
              • 1970-01-01
              • 2016-08-19
              • 2022-01-18
              • 2017-11-26
              • 2022-09-30
              相关资源
              最近更新 更多