打字稿:键入'string | 未定义”不能分配给“字符串”类型

asd*_*asd 23 typescript

当我将接口的任何属性设为可选时,将其成员分配给其他变量时出现如下错误

TS2322:键入“字符串| 未定义”不能分配给“字符串”类型。类型“未定义”不能分配给类型“字符串”。

interface Person {
  name?:string,
  age?:string,
  gender?:string,
  occupation?:string,
}

function getPerson(){
  let person = <Person>{name:"John"};
  return person;
}
let person: Person = getPerson();
let name1:string = person.name;//<<<Error here 
Run Code Online (Sandbox Code Playgroud)

如何解决此错误?

Har*_*rry 74

我知道这有点晚了,但是除了yannick使用的答案之外另一种方法!是将其转换为 string 从而告诉 TypeScript:我确定这是一个 string,因此将其转换。

let name1:string = person.name;//<<<Error here 
Run Code Online (Sandbox Code Playgroud)

let name1:string = person.name as string;
Run Code Online (Sandbox Code Playgroud)

这将使错误消失,但如果有任何机会这不是字符串,您将收到运行时错误......这是我们使用 TypeScript 来确保类型匹配并避免此类错误的原因之一编译时间

  • 这可能会使错误消失,但它永远不会改变变量的实际类型。 (5认同)
  • 无需解决方法,只需在尝试访问变量之前进行一项检查即可。这两种情况都很容易处理,错误就会消失,您的代码也会更安全。尝试一下:) (4认同)
  • 但这不是正确的方法,比如说如果我的名字中有数字“0”,它只会将其转换为字符串......相反,它应该抛出一个错误并说无效的名称。就我个人而言,我不会使用这种类型转换并专门检查对象中传递的类型..但我们都有不同的编码风格,欢迎对相同内容进行更多讨论 (2认同)
  • 您可以使用 `as string` 但输出不会与 `person.name!` 有任何不同 ([playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgCLIN7IA4H4BcyAzmFKAObIC+AUACYQIA2cUKCA9iCcnYagG4aNJhDDI4hEmRCUAvLwB02CUWKkKQ0eIBGUjb OQK6ygIQCgA))。您可能会遇到运行时错误,**但它不会在分配时发生**,正如本文所示。这个解决方案和顶级解决方案都是不好的做法;您应该检查 `name` 是否已定义或使用 `??`/`||` 合并为默认值。仅当您知道定义了某些“名称”时才执行此操作(如OP问题中所示)。 (2认同)

igo*_*igo 45

从 TypeScript 3.7 开始,您可以使用空合并运算符 ??。您可以将此功能视为在处理 null 或 undefined 时“回退”到默认值的一种方式

let name1:string = person.name ?? '';
Run Code Online (Sandbox Code Playgroud)

??操作可替代的用途||尝试使用默认值,并且可以与布尔值,数字等,其中处理时,可以使用时,||不能使用。

从 TypeScript 4 开始,您可以使用??=赋值运算符作为a ??= b替代a = a ?? b;


yan*_*976 35

现在,您可以使用恰好适合您的用例的非null断言运算符

它告诉TypeScript即使某些内容看起来可能为null,也可以相信它不是:

let name1:string = person.name!; 
//                            ^ note the exclamation mark here  
Run Code Online (Sandbox Code Playgroud)

  • 这是一个糟糕的建议!相反,帮助编译器弄清楚它。在访问属性之前添加一个检查,如果未设置名称,则抛出或返回。 (51认同)
  • 不同意最后两条评论...例如,如果该属性存在,我将有条件地渲染一个视觉元素,并且附加到该元素的是一个使用该属性的函数。不需要对函数内部的属性进行额外的非空检查 (8认同)
  • 我很震惊这样的事情需要存在。 (7认同)
  • 这正是我一直在寻找的! (6认同)
  • 直到 ESLint 不限制 *非空断言运算符* 的使用:) (6认同)
  • @eazy_g 听起来你做错了什么,如果你之前正确验证了空值,则不必进行非空检查。 (2认同)
  • 该运算符在加载环境变量时非常有用。例如,我的 .env 文件有一个我可以信任的值将始终存在,但是在需要字符串的第三方配置对象中使用我的 env 变量将始终引发此错误。在这种情况下,上面的解决方案就很完美了。 (2认同)
  • 对我来说,这看起来并不是一个糟糕的建议。我已经有一个问题,检查虚假值,所有变量都是直接键入的,并且打字稿仍然无法自行弄清楚。我现在有紧迫的任务需要紧急关注,我想弄清楚是什么打字稿配置设置导致了它。 (2认同)

And*_*ine 19

为了避免我使用的编译错误

let name1:string = person.name || '';
Run Code Online (Sandbox Code Playgroud)

然后验证空字符串。

  • 如果它是一个对象,那么它将是 {} 而不是 ' ' (3认同)
  • 这不是给定问题的解决方案。编译器可以帮助发现潜在的问题,而像“person.name ||”这样的赋值 ''` 只是将问题委托给其他地方。 (3认同)
  • 如果它不是字符串而是对象怎么办? (2认同)

gol*_*ins 13

处理此问题的一种更适合生产的方法是实际确保name存在。假设这是一群人参与的大型项目的最小示例,您不知道getPerson将来会发生怎样的变化。

if (!person.name) {
    throw new Error("Unexpected error: Missing name");
}

let name1: string = person.name;
Run Code Online (Sandbox Code Playgroud)

或者,您可以输入name1as string | undefined,然后处理undefined更进一步的情况。但是,提前处理意外错误通常会更好。

您还可以通过省略显式类型让 TypeScript 推断类型:例如,let name1 = person.name这仍将防止name1被重新分配为数字。

  • 这是最完整的答案,而不仅仅是一个可能导致以后出现问题的解决方法。谢谢你! (3认同)
  • @丁戈谢谢你!问题是“我该如何解决这个错误?” 但这不是正确的心态。 (2认同)

Kon*_*ret 7

这是了解正在发生的事情的快速方法:

当您执行以下操作时:

姓名?: 细绳

你对 TypeScript 说它是可选的。然而,当你这样做时:

let name1 : string = person.name; //<<<Error here 
Run Code Online (Sandbox Code Playgroud)

你没有给它一个选择。你需要有一个 Union 来反映未定义的类型:

let name1 : string | undefined = person.name; //<<<No error here 
Run Code Online (Sandbox Code Playgroud)

使用您的回答,我能够勾勒出以下内容,基本上是一个接口、一个类和一个对象。我发现这种方法更简单,如果你不介意的话。

// Interface
interface iPerson {
    fname? : string,
    age? : number,
    gender? : string,
    occupation? : string,
    get_person?: any
}

// Class Object
class Person implements iPerson {
    fname? : string;
    age? : number;
    gender? : string;
    occupation? : string;
    get_person?: any = function () {
        return this.fname;
    }
}

// Object literal
const person1 : Person = {
    fname : 'Steve',
    age : 8,
    gender : 'Male',
    occupation : 'IT'  
}

const p_name: string | undefined = person1.fname;

// Object instance 
const person2: Person = new Person();
person2.fname = 'Steve';
person2.age = 8;
person2.gender = 'Male';
person2.occupation = 'IT';

// Accessing the object literal (person1) and instance (person2)
console.log('person1 : ', p_name);
console.log('person2 : ', person2.get_person());
Run Code Online (Sandbox Code Playgroud)


Kar*_*ski 6

解决方案 1:删除显式类型定义

由于getPerson已经返回了Person带有名称的 a,因此我们可以使用推断的类型。

function getPerson(){
  let person = {name:"John"};
  return person;
}

let person = getPerson();
Run Code Online (Sandbox Code Playgroud)

如果我们要定义,person: Person我们就会丢失一条信息。我们知道getPerson返回一个具有名为 的非可选属性的对象name,但将其描述为Person会恢复可选性。

解决方案 2:使用更精确的定义

type Require<T, K extends keyof T> = T & {
  [P in K]-?: T[P]
};

function getPerson() {
  let person = {name:"John"};
  return person;
}

let person: Require<Person, 'name'> = getPerson();
let name1:string = person.name;
Run Code Online (Sandbox Code Playgroud)

解决方案 3:重新设计界面

所有属性都是可选的形状称为弱类型,通常是糟糕设计的指标。如果我们要制作name所需的财产,您的问题就会消失。

interface Person {
  name:string,
  age?:string,
  gender?:string,
  occupation?:string,
}
Run Code Online (Sandbox Code Playgroud)


Ali*_*i80 6

根据您的定义,Person.name可以为空,但name1不能为空。有两种情况:

Person.name 永远不会为空

通过使用告诉编译器您确定名称不为空 !

let name1: string = person.name!;
Run Code Online (Sandbox Code Playgroud)

Person.name 可以为空

如果名称为空,请指定默认值

let name1: string = person.name ?? "default name";
Run Code Online (Sandbox Code Playgroud)


Lyn*_*242 5

尝试事先找出实际值。如果person有效name,则将其分配给name1,否则分配undefined

let name1: string = (person.name) ? person.name : undefined;
Run Code Online (Sandbox Code Playgroud)

  • 嗯。如果我想做多项作业,那将太冗长了。有没有办法关闭这个特定的警告? (3认同)

小智 5

你可以这样做!

let name1:string = `${person.name}`;
Run Code Online (Sandbox Code Playgroud)

但记住name1可以是空字符串