当我将接口的任何属性设置为可选时,并将其成员分配给其他变量,如下所示:

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 

我得到如下错误:

TS2322: Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.

如何避免这个错误呢?


试着事先找出实际值。如果person有一个有效的名称,则将其赋值给name1,否则赋值为undefined。

let name1: string = (person.name) ? person.name : undefined;

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

因为getPerson已经返回了一个带有名称的Person,所以我们可以使用推断类型。

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

let person = getPerson();

如果我们定义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;

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

所有属性都是可选的形状称为弱类型,通常是设计不好的标志。如果我们将name作为必需属性,那么您的问题就解决了。

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

也有同样的问题。

我发现react-scrips添加了“strict”:true到tsconfig.json。

在我移除它之后,一切都很好。

Edit

需要警告的是,更改此属性意味着:

不再对潜在的运行时错误发出警告。

正如保罗在评论中指出的那样!谢谢!

只有在你完全理解它的影响时才使用"strict": false !


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

let name1:string = person.name || '';

然后验证空字符串。


现在您可以使用这里的非空断言操作符,它正是用于您的用例的。

它告诉TypeScript,即使某些东西看起来可能是null,它也可以相信你它不是:

let name1:string = person.name!; 
//                            ^ note the exclamation mark here  

我知道这有点晚了,但是除了yannick的答案,还有另一种方法可以使用!是将它转换为字符串,从而告诉TypeScript:我确定这是一个字符串,从而转换它。

let name1:string = person.name;//<<<Error here 

to

let name1:string = person.name as string;

这将使错误消失,但如果这不是一个字符串,你将得到一个运行时错误…这也是我们使用TypeScript来确保类型匹配并在编译时避免此类错误的原因之一。


你试图设置变量name1,女巫类型设置为严格的字符串(它必须是字符串),值从对象字段名,女巫值类型设置为可选字符串(它可以是字符串或未定义,因为问题符号)。如果你真的需要这种行为,你必须像这样改变name1的类型:

let name1: string | undefined = person.name;

一切都会好的;


这里有一个快速的方法来了解正在发生的事情:

当你做以下事情时:

的名字吗?:字符串

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

let name1 : string = person.name; //<<<Error here 

你没有给它选择的余地。你需要在它上面有一个反映未定义类型的联合:

let name1 : string | undefined = person.name; //<<<No error here 

使用您的回答,我能够概述以下内容,基本上是一个接口,一个类和一个对象。我发现这种方法更简单,如果你不这样做也没关系。

// 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());

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

let name1:string = person.name ?? '';

的? ?当尝试使用默认值时,操作符可以替换||的使用,并且可以在处理不能使用||的布尔值、数字等时使用。

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


你可以使用NonNullable实用程序类型:

例子

type T0 = NonNullable<string | number | undefined>;  // string | number
type T1 = NonNullable<string[] | null | undefined>;  // string[]

文档。


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

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

let name1: string = person.name;

或者,您可以将name1键入为字符串| undefined,并进一步处理未定义的情况。但是,最好尽早处理意外错误。

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


如果你想拥有nullable属性,将你的接口更改为:

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

如果未定义不是这种情况,可以从属性名前面删除问号(?)。


如果你从getPerson函数中移除<Person>强制转换,那么TypeScript将足够聪明地检测到你返回的对象确实具有name属性。

所以只要转一下:

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;

成:

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

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

let person = getPerson();
let name1: string = person.name;

如果你不能这样做,那么你将不得不使用@yannick1976建议的“确定赋值断言操作符”:

let name1: string = person.name!;

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

Person.name从不为空

告诉编译器你确定名称不是空的使用!

let name1: string = person.name!;

Person.name可以为空

在case name为null时指定一个默认值

let name1: string = person.name ?? "default name";

你可以这样做!

let name1:string = `${person.name}`;

但是记住name1可以是空字符串


  @Input()employee!:string ;
announced:boolean=false;
confirmed:boolean=false;
task:string="<topshiriq yo'q>";

  constructor(private taskService:TaskService ) {
    taskService.taskAnnon$.subscribe(
      task => {
        this.task=task;
        this.announced=true;
        this.confirmed=false;
      }
    )
  }

  ngOnInit(): void {
  }
  confirm(){
   this.confirmed=true
   this.taskService.confirimTask(this.employee);
  }
}

这是我发现的检查属性是否未定义而不生成警告的唯一解决方案

type NotUndefined<T, K extends keyof T> = T & Record<K, Exclude<T[K], undefined>>;
function checkIfKeyIsDefined<T, K extends keyof T>(item: T, key: K): item is NotUndefined<T, K> {
    return typeof item === 'object' && item !== null && typeof item[key] !== 'undefined';
}

用法:


interface Listing { 
    order?: string
    ...
}
obj = {..., order: 'pizza'} as Listing
if(checkIfKeyIsDefined(item: obj, 'order')) {
    order.toUpperCase() //no undefined warning O.O
}

原来的答案


我认为使用Karol Majewski提到的Require非常好。另一种实现相同的方法是使用交集类型(实际上由Require在内部使用)

function getPerson(): Person & {name: string} {
  const person = {name:"John"};
  return person;
}

const person = getPerson();
const name1: string = person.name;

使用Require或交集类型的优点是,我们不会像非空断言操作符那样推翻typescript编译器。


在我的特定情况下发生在我身上的一件事(但它与这里问到的具体问题无关)是,我在文件中导入了错误的类型,因为类型被称为完全相同,但定义了不同的属性。

一旦我导入了正确的类型,所有的问题都消失了。

我希望这能帮助那些和我面临同样问题的人。