Definição de tipo no literal do objeto no TypeScript


344

Nas classes TypeScript, é possível declarar tipos para propriedades, por exemplo:

class className {
  property: string;
};

Como declarar o tipo de uma propriedade em um objeto literal?

Eu tentei o seguinte código, mas ele não compila:

var obj = {
  property: string;
};

Estou recebendo o seguinte erro:

O nome 'string' não existe no escopo atual

Estou fazendo algo errado ou isso é um bug?

Respostas:


423

Você está bem perto, basta substituir o =com um :. Você pode usar um tipo de objeto literal (consulte a seção 3.5.3), ou uma interface. O uso de um literal de tipo de objeto é próximo ao que você possui:

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

Mas você também pode usar uma interface

interface MyObjLayout {
    property: string;
}

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

14
A opção DRY (Não se repita) de Rick Love com o operador de elenco parece muito mais limpa. Apenas comentando, não diminuindo ...
spechter

Eu sei que isso foi respondido há um tempo atrás, mas estou enganado nas suposições de que problemas podem surgir quando a classe tem métodos e propriedades? Como a classe não é inicializada e apenas atribuímos propriedades, chamar um método na classe causará uma exceção nula. Basicamente, o objeto que criamos apenas 'atua' como a classe porque atribuímos seu tipo, mas na verdade não é uma instância dessa classe. Ou seja, precisamos criar a classe com a palavra-chave 'new'. E então voltamos ao quadrado 1, já que não podemos fazer algo como nova classe () {prop: 1}; em TS como podemos em C #, por exemplo.
DeuxAlpha

@DeuxAlpha Atribui a uma interface que não pode ter métodos. Não acredito que você possa atribuir a uma classe como esta.
Mog0 21/05/19

link para especificação seria bom :)
Ahong

@DeuxAlpha está criando um literal de objeto, não um objeto de classe. Também uma interface pode ter métodos. Se sua interface define um método, o literal do objeto também deve defini-lo - não será nulo. O literal do objeto deve atender a tudo o que a interface define ou o sistema de tipos mostrará um erro.
Rick Love

291

Atualizar 2019-05-15 (Padrão de código aprimorado como alternativa)

Depois de muitos anos usando conste se beneficiando de um código mais funcional, eu recomendaria não usar o abaixo na maioria dos casos. (Ao criar objetos, forçar o sistema de tipos a um tipo específico, em vez de permitir inferir tipos, geralmente indica que algo está errado).

Em vez disso, eu recomendaria usar as constvariáveis ​​o máximo possível e, em seguida, componha o objeto como a etapa final:

const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
  • Isso digitará tudo corretamente, sem a necessidade de digitar explicitamente.
  • Não há necessidade de redigitar os nomes dos campos.
  • Isso leva ao código mais limpo da minha experiência.
  • Isso permite que o compilador forneça mais verificação de estado (por exemplo, se você retornar em vários locais, o compilador garantirá que o mesmo tipo de objeto seja sempre retornado - o que incentiva você a declarar todo o valor de retorno em cada posição - oferecendo uma visão perfeitamente clara intenção desse valor).

Adição 2020-02-26

Se você realmente precisa de um tipo que possa ser inicializado com preguiça: Marque como um tipo de união anulável (nulo ou Tipo). O sistema de tipos impedirá que você o use sem antes garantir que ele tenha um valor.

Em tsconfig.json, certifique-se de habilitar verificações nulas estritas:

"strictNullChecks": true

Em seguida, use esse padrão e permita que o sistema de tipos o proteja contra acesso nulo / indefinido acidental:



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
    }
}

Não faça o abaixo em 99% dos casos:

Atualização 10/02/2016 - Para lidar com o TSX (Obrigado @ Josh)

Use o asoperador para TSX.

var obj = {
    property: null as string
};

Um exemplo mais longo:

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

Resposta original

Use o operador de conversão para tornar isso sucinto (convertendo nulo para o tipo desejado).

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

Um exemplo mais longo:

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

Isso é muito melhor do que ter duas partes (uma para declarar tipos e a segunda para declarar padrões):

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

10
Se você estiver usando TSX (TS com JSX), não poderá usar a nomeação de colchetes angulares, para que essas linhas se tornem algo property: null as stringem que a diferença importante seja o asoperador.
Josh

2
@RickLove Isso realmente restringe o tipo da variável de objeto ou é apenas uma maneira de especificar os tipos quando atribuídos? Em outras palavras, depois de atribuir à variável callno seu segundo exemplo, você pode atribuir um tipo completamente diferente a ela?
Daniel Griscom

3
Esta resposta deve ser feita
Drew R:

4
não está funcionando. Error:(33, 15) TS2352:Type 'null' cannot be converted to type 'string'.
slideshowp2

2
Isso é apenas um abuso de um recurso do idioma ou é realmente legítimo? Você poderia fornecer um link para a leitura de mor para documentos oficiais? Obrigado!
Qwerty

31

Estou surpreso que ninguém tenha mencionado isso, mas você pode simplesmente criar uma interface chamada ObjectLiteral, que aceita key: valuepares de tipos string: any:

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

Então você usaria, assim:

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

Um bônus adicional é que você pode reutilizar essa interface quantas vezes for necessário, em quantos objetos desejar.

Boa sorte.


3
Essa é uma péssima idéia, pois torna o sistema de tipos inútil. O objetivo do texto datilografado é permitir que o sistema de texto ajude a evitar erros e também a fornecer uma melhor funcionalidade de preenchimento automático de ferramentas - isso basicamente desativa todos os benefícios do texto datilografado. Seria melhor não usar nenhuma interface no exemplo acima.
Rick Love

11
@ RickLove Eu discordo totalmente. Isso tem sido excepcionalmente útil quando várias propriedades são opcionais, mas ainda queremos definir claramente todos os tipos (por exemplo, contendo argumentos para uma função). Isso poderia ser simplesmente visto como açúcar sintático e literalmente funciona como um operador de spread para as propriedades da Interfaces .
CPHPython 11/10/19

@CPHPython, por que não especificar apenas os parâmetros opcionais com seus tipos específicos? A única vez que isso faria sentido é se os nomes não forem conhecidos no tempo do código (ou seja, eles vêm de um banco de dados ou de uma fonte externa). Também para combinações complexas de argumentos, os tipos de união funcionam muito bem. Se você estiver codificando com nomes específicos, eles deverão ser definidos, se possível. Se são apenas dados que não afetam a lógica, é claro que não são do sistema de tipos.
Rick Love

11
@RickLove "os nomes não são conhecidos no tempo do código" -> este é um exemplo prático e bom, os valores desses tipos de chaves são conhecidos e são todos iguais (por exemplo, string ). Lembre-se de que eu estava apenas defendendo o uso da [key: string]parte, e não a parte qualquer como a definição de tipo do valor ... Isso realmente eliminaria a utilidade dos tipos.
CPHPython

11
Como eu iria se eu quisesse permitir que strings e números fossem adicionados, mas não outros tipos?
DonkeyBanana

14

Se você estiver tentando escrever uma anotação de tipo, a sintaxe é:

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

Se você estiver tentando escrever um objeto literal, a sintaxe é:

var x = { property: 'hello' };

Seu código está tentando usar um nome de tipo em uma posição de valor.


10
Você var x: { property: string; } = ...;é uma provocação! Eu esperava que as reticências fossem uma sintaxe válida para ser uma abreviação var x: { property: string; } = { property: 'hello' };.
Jason Kleban

10

No TypeScript, se estamos declarando objeto, usaríamos a seguinte sintaxe:

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

Por exemplo:

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

7

Se você está tentando adicionar digitações a um objeto literal destruído , por exemplo, nos argumentos de uma função, a sintaxe é:

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

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

5

Se suas propriedades tiverem o mesmo tipo, você poderá usar o tipo de utilitário predefinido Record:

type Keys = "property" | "property2"

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

É claro que você pode ir além e definir um tipo personalizado para seus valores de propriedade:

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
Exatamente o que eu precisava. Obrigado! Texto datilografado para a vitória!
Audacitus

Eu também usaria em letvez de varaqui.
Fwd079 12/06

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
Ei, e bem-vindo ao SO! Você pode expandir sua resposta? Seria útil explicar como / por que isso funciona.
party-ring

1

No seu código:

var obj = {
  myProp: string;
};

Na verdade, você está criando um literal de objeto e atribuindo a sequência de variáveis ​​à propriedade myProp. Embora seja uma prática muito ruim, esse seria realmente um código TS válido (não use isso!):

var string = 'A string';

var obj = {
  property: string
};

No entanto, o que você deseja é que o literal do objeto seja digitado. Isso pode ser alcançado de várias maneiras:

Interface:

interface myObj {
    property: string;
}

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

Tipo personalizado:

type myObjType = {
    property: string
};

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

Operador de elenco:

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

Tipo de objeto literal:

var obj: { property: string; } = { property: "Mystring" };
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.