Mecanografiado: Escriba 'cadena | undefined 'no se puede asignar al tipo' cadena '


100

Cuando hago que cualquier propiedad de una interfaz sea opcional, aparece un error como seguir al asignar su miembro a alguna otra variable

TS2322: Escriba 'cadena | undefined 'no se puede asignar al tipo' cadena '. El tipo 'indefinido' no se puede asignar al tipo 'cadena'.

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 

¿Cómo puedo solucionar este error?

Respuestas:


182

Ahora puede usar el operador de aserción no nula que está aquí exactamente para su caso de uso.

Le dice a TypeScript que aunque algo parece que podría ser nulo, puede confiar en que no lo es:

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

3
¡Eso es exactamente lo que estaba buscando!
Shautieh

1
Hasta que ESLint no restrinja el uso del operador de aserción no nula :)
Anatoly

3
Me sorprende que algo así tenga que existir. 🤯
GONeale

@GONeale hay muchos casos en los que el transpilador no puede darse cuenta por sí solo de que un valor no puede ser nulo. Un ejemplo es un valor establecido, por ejemplo, por Angular, y Angular tiene su propia forma de garantizar que el valor no puede ser nulo.
yannick1976

18
¡Este es un consejo terrible! En su lugar, ayude al compilador a averiguarlo. Agregue un cheque antes de acceder a la propiedad, donde puede tirar o devolver si el nombre no está establecido.
geon

56

Para evitar el error de compilación que usé

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

Y luego valide la cadena vacía.


1
¿Y si no es una cadena sino un objeto?
Anatoly

1
Si es un objeto, en lugar de "" será {}
Kriti

Esto es algo que se puede hacer cuando está encadenando los valores y se obtiene este tipo de errorOptional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong.
tHeSiD

26

Sé que esta es una respuesta un poco tardía, pero otra forma además de la respuesta de yannick https://stackoverflow.com/a/57062363/6603342 para usar. es convertirlo como una cadena y decirle a TypeScript que estoy seguro de que esta es una cadena que convierte

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

a

let name1:string = person.name as string;

Esto hará que el error desaparezca, pero si por casualidad esto no es una cadena , obtendrá un error en tiempo de ejecución, que es una de las razones por las que estamos usando TypeScript para asegurarnos de que el tipo coincida y evitar tales errores en tiempo de compilación .


Esto puede hacer que el error desaparezca, pero nunca cambia el tipo real de variable.
Mansi

Lo siento, pero creo que está bastante equivocado, como he mencionado, esto de hecho cambiará el tipo, ya que generará una excepción de tiempo de ejecución si el tipo no es una cadena o no tiene un método toString. Esto convertirá todo lo que esté allí en una cadena, es decir, si tiene un número que intentará llamar a la función .toString de lo que tenga allí.
Harry

Pero esa no será la forma correcta, digamos que si mi nombre tiene el número "0", simplemente lo convertiría en una cadena ... Más bien debería arrojar un error y decir un nombre no válido. Personalmente, no usaría este encasillado y comprobaría especialmente el tipo pasado en el objeto ... pero todos tenemos diferentes estilos de codificación, abiertos para más discusión sobre el mismo
Mansi

Sí, pero esto completamente fuera del contexto de la pregunta que se dio a esta respuesta. No sabemos si se trata de un formulario si no hay pasos para filtrarlo de antemano. Además, lo que está sugiriendo sería imposible en esa etapa sin algunas soluciones realmente sucias.
Harry

No hay necesidad de soluciones alternativas, solo una verificación antes de intentar acceder a la variable. Ambos casos se manejarían fácilmente, los errores desaparecerían y su código estaría más seguro. Pruébalo :)
Mansi

21

A partir de TypeScript 3.7, puede utilizar el operador de fusión nula ?? . Puede pensar en esta función como una forma de "retroceder" a un valor predeterminado cuando se trata de nulos o indefinidos

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

El ??operador puede reemplazar los usos de ||cuando se intenta usar un valor predeterminado y se puede usar cuando se trata de valores booleanos, números, etc., donde ||no se pueden usar.

A partir de TypeScript 4, puede utilizar el ??=operador de asignación como a ??= balternativa aa = a ?? b;


7

intente averiguar de antemano cuál es el valor real. Si persontiene un válido name, asígnelo a name1, de lo contrario, asigne undefined.

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

2
hmmm. Eso será demasiado detallado si quiero hacer varias asignaciones. ¿Hay alguna forma de desactivar esta advertencia en particular?
asdasd

Lamentablemente, no, excepto que lo haga nameobligatorio al eliminar el signo de interrogación.
Lynx 242

PD: ¿Qué versión de TypeScript usas? Yo uso 3.2.4 y no me encuentro con este mensaje. En mi clase de prueba, el código se compila correctamente.
Lynx 242

misma versión. Recibo este error en webstorm pero no en vscode
asdasd

Eso es extraño, porque uso IntelliJ, que se supone que es similar a WebStorm cuando se trata de la aplicación de reglas TS.
Lynx 242

5

Una forma más preparada para la producción de manejar esto es asegurarse de que nameesté presente. Suponiendo que este es un ejemplo mínimo de un proyecto más grande en el que está involucrado un grupo de personas, no sabe cómo getPersoncambiará en el futuro.

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

let name1: string = person.name;

Alternativamente, puede escribir name1como string | undefinedy manejar casos de undefinedmás abajo. Sin embargo, normalmente es mejor manejar los errores inesperados antes.

También puede permitir que TypeScript infiera el tipo omitiendo el tipo explícito: let name1 = person.nameesto aún evitará que name1se reasigne como un número, por ejemplo.


3

A continuación, le mostramos una forma rápida de conocer lo que está sucediendo:

Cuando hiciste lo siguiente:

¿nombre? : cuerda

Le estaba diciendo a TypeScript que era opcional. Sin embargo, cuando lo hizo:

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

No dejaste otra opción. Necesitaba tener una Unión que refleje el tipo indefinido:

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

Usando su respuesta, pude esbozar lo siguiente, que es básicamente, una interfaz, una clase y un objeto. Encuentro este enfoque más simple, no importa si usted no lo hace.

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

2

Está intentando establecer la variable name1, el tipo establecido como una cadena estricta (DEBE ser una cadena) con el valor del campo del objeto name, el tipo de valor establecido como una cadena opcional (puede ser una cadena o indefinido, debido al signo de pregunta). Si realmente necesita este comportamiento, debe cambiar el tipo de name1este:

let name1: string | undefined = person.name;

Y estará bien;


0

Tenga en cuenta que si la variable es de una matriz o desestructuración, puede proporcionar valores predeterminados:

const { DB_HOST = "localhost", DB_PORT: "5432" } = process.env

db.connect(DB_HOST, DB_PORT);
const connectToDb = (host = "localhost", port: "5432") => db.connect(host, port);


0

Puede utilizar el NonNullabletipo de utilidad:

Ejemplo

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

Docs .


-1

Solución 1: elimine la definición de tipo explícita

Como getPersonya devuelve un Personcon un nombre, podemos usar el tipo inferido.

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

let person = getPerson();

Si tuviéramos que definir person: Person, perderíamos una pieza de información. Sabemos que getPersondevuelve un objeto con una propiedad no opcional llamada name, pero describirlo como Persondevolvería la opcionalidad.

Solución 2: use una definición más precisa

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;

Solución 3: rediseña tu interfaz

Una forma en la que todas las propiedades son opcionales se denomina tipo débil y generalmente es un indicador de mal diseño. Si tuviéramos que hacer nameuna propiedad requerida, su problema desaparecería.

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

-14

Tuve el mismo problema.

Me entero de que reaccione-scripts añaden "strict": truea tsconfig.json.

Después de quitarlo, todo funciona muy bien.

Editar

Debe advertir que cambiar esta propiedad significa que usted:

ya no se le advierte sobre posibles errores en tiempo de ejecución.

como ha señalado PaulG en los comentarios. Gracias :)

¡Úselo "strict": falsesolo si comprende completamente lo que afecta!


9
Esto puede "funcionar". Pero solo funciona en el sentido de que ya no se le advierte sobre posibles errores de tiempo de ejecución. Animaría
PaulG

5
Debo mencionar que el OP preguntó: "¿Cómo puedo solucionar este error?" no, "¿Cómo soluciono este problema?"
devinbost

Simplemente agregando name: string = undefined (como lo recomiendan paquetes como json2typescript) arrojará el error 2322 a menos que desactive estrictamente, que es lo que la mayoría de la gente necesitará hacer. Esta respuesta aceptada es clara, correcta y merece muchos más votos.
AUSTX_RJL
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.