Definición de tipo en literal de objeto en TypeScript


344

En las clases de TypeScript es posible declarar tipos para propiedades, por ejemplo:

class className {
  property: string;
};

¿Cómo declarar el tipo de una propiedad en un objeto literal?

He intentado el siguiente código pero no se compila:

var obj = {
  property: string;
};

Recibo el siguiente error:

El nombre 'cadena' no existe en el ámbito actual

¿Estoy haciendo algo mal o es un error?

Respuestas:


423

Estás bastante cerca, solo necesitas reemplazarlo =con a :. Puede usar un literal de tipo de objeto (consulte la sección de especificaciones 3.5.3) o una interfaz. Usar un tipo de objeto literal está cerca de lo que tienes:

var obj: { property: string; } = { property: "foo" };

Pero también puedes usar una interfaz

interface MyObjLayout {
    property: string;
}

var obj: MyObjLayout = { property: "foo" };

14
La opción DRY (Don't Repeat Yourself) de @Rick Love con el operador de reparto parece mucho más ordenada. Solo comentando, no votando mal ...
spechter

Sé que esto se ha respondido hace un tiempo, pero ¿estoy equivocado en los supuestos de que pueden surgir problemas cuando la clase tiene métodos y propiedades? Como la clase no se inicializa y solo asignamos propiedades, llamar a un método en la clase provocará una excepción nula. Básicamente, el objeto que creamos solo 'actúa' como la clase porque asignamos su tipo, pero en realidad no es una instancia de esa clase. Es decir, necesitamos crear la clase con la palabra clave 'nueva'. Y luego volvemos al cuadrado 1, ya que no podemos hacer algo como new class () {prop: 1}; en TS como podemos en C #, por ejemplo.
DeuxAlpha

@DeuxAlpha Esto se asigna a una interfaz que no puede tener métodos. No creo que puedas asignar a una clase como esta.
Mog0

enlace a la especificación sería bueno :)
ahong

@DeuxAlpha esto está creando un objeto literal, no un objeto de clase. También una interfaz puede tener métodos. Si su interfaz define un método, entonces el literal del objeto también debe definirlo, no será nulo. El objeto literal debe cumplir con todo lo que la interfaz define o el sistema de tipos mostrará un error.
Rick Love

291

Actualización 2019-05-15 (Patrón de código mejorado como alternativa)

Después de muchos años de usar consty beneficiarse de un código más funcional, recomendaría no usar el siguiente en la mayoría de los casos. (Al construir objetos, forzar el sistema de tipos a un tipo específico en lugar de permitir que infiera tipos a menudo es una indicación de que algo está mal).

En cambio, recomendaría usar constvariables tanto como sea posible y luego componer el objeto como el paso final:

const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
  • Esto escribirá correctamente todo sin necesidad de escribir explícitamente.
  • No es necesario volver a escribir los nombres de campo.
  • Esto lleva al código más limpio de mi experiencia.
  • Esto permite que el compilador proporcione más verificación de estado (por ejemplo, si regresa en varias ubicaciones, el compilador se asegurará de que siempre se devuelva el mismo tipo de objeto, lo que lo alienta a declarar el valor de retorno completo en cada posición), lo que le da un claro perfectamente intención de ese valor).

Suma 2020-02-26

Si realmente necesita un tipo que se pueda inicializar perezosamente: márquelo como un tipo de unión anulable (nulo o tipo). El sistema de tipos evitará que lo use sin asegurarse primero de que tenga un valor.

En tsconfig.json, asegúrese de habilitar comprobaciones nulas estrictas:

"strictNullChecks": true

Luego use este patrón y permita que el sistema de tipos lo proteja del acceso nulo / indefinido accidental:



const state = {
    instance: null as null | ApiService,
    // OR
    // instance: undefined as undefined | ApiService,

};

const useApi = () => {
    // If I try to use it here, the type system requires a safe way to access it

    // Simple lazy-initialization 
    const api = state?.instance ?? (state.instance = new ApiService());
    api.fun();

    // Also here are some ways to only access it if it has value:

    // The 'right' way: Typescript 3.7 required
    state.instance?.fun();

    // Or the old way: If you are stuck before Typescript 3.7
    state.instance && state.instance.fun();

    // Or the long winded way because the above just feels weird
    if (state.instance) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans way
    if (state.instance != null) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans 
    // AND I was told to always use triple === in javascript even with null checks way
    if (state.instance !== null && state.instance !== undefined) { state.instance.fun(); }
};

class ApiService {
    fun() {
        // Do something useful here
    }
}

No haga lo siguiente en el 99% de los casos:

Actualización 2016-02-10 - Para manejar TSX (Gracias @ Josh)

Use el asoperador para TSX.

var obj = {
    property: null as string
};

Un ejemplo más largo:

var call = {
    hasStarted: null as boolean,
    hasFinished: null as boolean,
    id: null as number,
};

Respuesta original

Use el operador de conversión para hacer esto conciso (convirtiendo nulo al tipo deseado).

var obj = {
    property: <string> null
};

Un ejemplo más largo:

var call = {
    hasStarted: <boolean> null,
    hasFinished: <boolean> null,
    id: <number> null,
};

Esto es mucho mejor que tener dos partes (una para declarar tipos, la segunda para declarar valores predeterminados):

var callVerbose: {
    hasStarted: boolean;
    hasFinished: boolean;
    id: number;
} = {
    hasStarted: null,
    hasFinished: null,
    id: null,
};

10
Si está utilizando TSX (TS con JSX), no puede usar el nombre del paréntesis angular, por lo que esas líneas se convierten en algo así como property: null as stringdonde la diferencia importante es el asoperador.
Josh

2
@RickLove ¿Esto realmente restringe el tipo de la variable del objeto, o esta es solo una forma de especificar los tipos cuando se asigna? En otras palabras, después de asignar a la variable callen su segundo ejemplo, ¿puede asignarle un tipo completamente diferente?
Daniel Griscom

3
Esta debería ser la respuesta
Drew R

44
no funciona. Error:(33, 15) TS2352:Type 'null' cannot be converted to type 'string'.
slideshowp2

2
¿Es esto solo un abuso de una característica del lenguaje o es realmente Legit? ¿Podría proporcionar un enlace para leer más a los documentos oficiales? ¡Gracias!
Qwerty

31

Me sorprende que nadie haya mencionado esto, pero podría crear una interfaz llamada ObjectLiteralque acepte key: valuepares de tipos string: any:

interface ObjectLiteral {
  [key: string]: any;
}

Entonces lo usarías, así:

let data: ObjectLiteral = {
  hello: "world",
  goodbye: 1,
  // ...
};

Una ventaja adicional es que puede reutilizar esta interfaz tantas veces como lo necesite, en tantos objetos como desee.

Buena suerte.


3
Esta es una mala idea, hace que el sistema de tipos no tenga valor. El objetivo de typecript es permitir que el sistema de tipos ayude a prevenir errores y también a proporcionar una mejor funcionalidad de autocompletar en herramientas; esto básicamente deshabilita todos los beneficios de Typecript. Sería mejor no usar ninguna interfaz en el ejemplo anterior.
Rick Love

1
@ RickLove Estoy totalmente en desacuerdo. Esto ha sido excepcionalmente útil cuando varias propiedades son opcionales pero aún queremos definir claramente todos los tipos (por ejemplo, que contienen argumentos para una función). Esto podría verse simplemente como azúcar sintáctica y literalmente funciona como un operador de propagación para las propiedades de las interfaces .
CPHPython

@CPHPython ¿por qué no solo especificar los parámetros opcionales con sus tipos específicos? El único momento en que esto tendría sentido es si los nombres no se conocen en el momento del código (es decir, provienen de una base de datos o fuente externa). También para combinaciones complejas de argumentos, los tipos de unión funcionan muy bien. Si está codificando contra nombres específicos, deben definirse si es posible. Si solo se trata de datos que no afectan la lógica, entonces, por supuesto, omítalos del sistema de tipos.
Rick Love

1
@RickLove "los nombres no se conocen en el momento del código" -> este es un buen ejemplo práctico, los valores de esos tipos de claves son conocidos y todos son iguales (por ejemplo, cadena ). Tenga en cuenta que solo estaba abogando por el uso de la [key: string]parte, no de ninguna parte como la definición de tipo del valor ... Eso realmente eliminaría la utilidad de los tipos.
CPHPython

1
¿Cómo procedería si quisiera permitir que se agreguen cadenas y números pero no otros tipos?
DonkeyBanana

14

Si está intentando escribir una anotación de tipo, la sintaxis es:

var x: { property: string; } = ...;

Si está intentando escribir un objeto literal, la sintaxis es:

var x = { property: 'hello' };

Su código está intentando usar un nombre de tipo en una posición de valor.


10
Tu var x: { property: string; } = ...;es una tomadura de pelo! Esperaba que la elipsis fuera una sintaxis válida para ser abreviada var x: { property: string; } = { property: 'hello' };.
Jason Kleban

10

En TypeScript, si estamos declarando un objeto, usaríamos la siguiente sintaxis:

[access modifier] variable name : { /* structure of object */ }

Por ejemplo:

private Object:{ Key1: string, Key2: number }

7

Si está intentando agregar tipings a un objeto literal desestructurado , por ejemplo en argumentos de una función, la sintaxis es:

function foo({ bar, baz }: { bar: boolean, baz: string }) {
  // ...
}

foo({ bar: true, baz: 'lorem ipsum' });

5

Si sus propiedades tienen el mismo tipo, puede usar un tipo de utilidad predefinido Record:

type Keys = "property" | "property2"

var obj: Record<Keys, string> = {
  property: "my first prop",
  property2: "my second prop",
};

Por supuesto, puede ir más allá y definir un tipo personalizado para los valores de su propiedad:

type Keys = "property" | "property2"
type Value = "my prop" | "my other allowed prop"

var obj: Record<Keys, Value> = {
  property: "my prop",
  property2: "my second prop", // TS Error: Type '"my second prop"' is not assignable to type 'Value'.
};

2
Exactamente lo que necesitaba. ¡Gracias! Mecanografiado para la victoria!
Audacitus

También lo usaría en letlugar de varaquí.
Fwd079

3
// Use ..

const Per = {
  name: 'HAMZA',
  age: 20,
  coords: {
    tele: '09',
    lan: '190'
  },
  setAge(age: Number): void {
    this.age = age;
  },
  getAge(): Number {
    return age;
  }
};
const { age, name }: { age: Number; name: String } = Per;
const {
  coords: { tele, lan }
}: { coords: { tele: String; lan: String } } = Per;

console.log(Per.getAge());

3
Hola y bienvenidos a SO! ¿Puede ampliar su respuesta? Sería útil explicar cómo / por qué funciona esto.
anillo de fiesta el

1

En su código:

var obj = {
  myProp: string;
};

En realidad, está creando un objeto literal y asignando la cadena variable a la propiedad myProp. Aunque es una práctica muy mala, este sería un código TS válido (¡no lo uses!):

var string = 'A string';

var obj = {
  property: string
};

Sin embargo, lo que quiere es que se escriba el objeto literal. Esto se puede lograr de varias maneras:

Interfaz:

interface myObj {
    property: string;
}

var obj: myObj = { property: "My string" };

Tipo personalizado:

type myObjType = {
    property: string
};

var obj: myObjType = { property: "My string" };

Operador de reparto:

var obj = {
    myString: <string> 'hi',
    myNumber: <number> 132,
};

Tipo de objeto literal:

var obj: { property: string; } = { property: "Mystring" };
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.