Визначення типу в літералі об'єкта в TypeScript


345

У класах TypeScript можна оголосити типи для властивостей, наприклад:

class className {
  property: string;
};

Як оголосити тип властивості в об'єкті буквально?

Я спробував наступний код, але він не компілюється:

var obj = {
  property: string;
};

Я отримую таку помилку:

Назва "рядок" не існує в поточному діапазоні

Я щось роблю неправильно чи це помилка?

Відповіді:


423

Ви досить близько, вам просто потрібно замінити =з :. Можна використовувати літеральний тип об’єкта (див. Специфікацію розділу 3.5.3) або інтерфейс. Використання буквеного типу об’єкта близьке до того, що у вас є:

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

Але ви також можете використовувати інтерфейс

interface MyObjLayout {
    property: string;
}

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

14
Варіант DRY (не повторюй себе) від @Rick Love з оператором лиття здається набагато охайнішим. Просто коментуючи, не зволікаючи ...
spechter

Я знаю, що на це відповіли деякий час тому, але чи помиляюсь у припущеннях, що проблеми можуть виникати, коли клас має методи, а також властивості? Оскільки клас не ініціалізується і ми присвоюємо лише властивості, виклик методу в класі призведе до нульового виключення. В основному, об'єкт, який ми створюємо, лише "діє" як клас, тому що ми присвоюємо його типу, але насправді це не примірник цього класу. Тобто нам потрібно створити клас за допомогою "нового" ключового слова. І ми повертаємося до площі 1, оскільки ми не можемо зробити щось на кшталт нового класу () {prop: 1}; наприклад, в TS, як ми можемо, в C #.
DeuxAlpha

@DeuxAlpha Це призначено для інтерфейсу , який не може мати методи. Я не вірю, що ви можете призначити такий клас.
Mog0

посилання на специфікацію було б непогано :)
Ahong

@DeuxAlpha це створення об'єкта буквально, а не об’єкт класу. Також інтерфейс може мати методи. Якщо ваш інтерфейс визначає метод, то літерал об'єкта також повинен його визначати - він не буде нульовим. Об'єктний буквал повинен виконувати все, що визначає інтерфейс, або система типу покаже помилку.
Rick Love

291

Оновити 2019-05-15 (вдосконалений шаблон коду як альтернатива)

Після багатьох років використання constта отримання користі від більш функціонального коду я б рекомендував не використовувати наведене нижче у більшості випадків. (Будуючи об'єкти, примушування системи типів до певного типу замість того, щоб робити висновки типів, часто є ознакою того, що щось не так).

Натомість я рекомендував би constякомога більше використовувати змінні, а потім скласти об'єкт як останній крок:

const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
  • Це дозволить правильно ввести все без необхідності явного введення тексту.
  • Не потрібно повторно вводити назви полів.
  • Це призводить до найчистішого коду з мого досвіду.
  • Це дозволяє компілятору забезпечити більшу перевірку стану (наприклад, якщо ви повернетесь у декілька місцеположень, компілятор забезпечить, щоб завжди повертався один і той же тип об'єкта - що спонукає вас оголосити все повернене значення на кожній позиції - надаючи ідеально чітке намір цієї цінності).

Доповнення 2020-02-26

Якщо вам дійсно потрібен тип, який ви можете лінь ініціалізувати: Позначте, що це зведений тип об'єднання (null або Type). Система типу не дозволить вам використовувати її, не попередньо переконавшись, що вона має значення.

У цьому tsconfig.jsonпереконайтеся, що ви включили строгі нульові перевірки:

"strictNullChecks": true

Потім використовуйте цей шаблон і дозвольте системі типу захистити вас від випадкового нульового / невизначеного доступу:



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

Не робіть наступного в 99% випадків:

Оновлення 2016-02-10 - для обробки TSX (спасибі @Josh)

Використовуйте asоператора для TSX.

var obj = {
    property: null as string
};

Більш довгий приклад:

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

Оригінальний відповідь

Скористайтеся оператором кадру, щоб зробити цей короткий (за допомогою нульового значення нульового типу).

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

Більш довгий приклад:

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

Це набагато краще, ніж мати дві частини (одна для оголошення типів, друга для оголошення за замовчуванням):

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

10
Якщо ви використовуєте TSX (TS з JSX), ви не можете використовувати назви кутової дужки, тому ці рядки стають чимось на зразок, property: null as stringколи важливою відмінністю є asоператор.
Джош

2
@RickLove Чи насправді це обмежує тип змінної об'єкта, чи це лише спосіб вказати типи при призначенні? Іншими словами, після того, як ви призначите змінну callу своєму другому прикладі, чи можете ви призначити їй зовсім інший тип?
Даніель Гріском

3
Це має відповісти
Дрю Р

4
не працює. Error:(33, 15) TS2352:Type 'null' cannot be converted to type 'string'.
слайд-

2
Це просто зловживання якоюсь особливістю мови чи це насправді легіт? Чи можете ви надати посилання для читання mor до офіційних документів? Дякую!
Qwerty

31

Я здивований, що ніхто не згадував про це, але ви могли просто створити інтерфейс під назвою ObjectLiteral, який приймає key: valueпари типу string: any:

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

Тоді ви використовуєте його так:

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

Додатковий бонус полягає в тому, що ви можете повторно використовувати цей інтерфейс стільки разів, скільки вам потрібно, на стільки об'єктів, які ви хочете.

Удачі.


3
Це погана ідея, вона робить систему типу нікчемною. Сенс typecript полягає в тому, щоб дозволити системі типів запобігти помилкам, а також забезпечити кращу функцію автозаповнення в інструментах - це в основному виключає всі переваги Typescript. Було б краще не використовувати жоден інтерфейс у наведеному вище прикладі.
Rick Love

1
@RickLove Я абсолютно не згоден. Це було надзвичайно корисно, коли декілька властивостей необов’язкові, але ми все ще хочемо чітко визначити всі типи всередині (наприклад, що містять аргументи для функції). Це можна просто розглядати як синтаксичний цукор і буквально працює як оператор розповсюдження властивостей інтерфейсів .
CPHPython

@CPHPython чому не просто вказати необов'язкові параметри з їх конкретними типами? Єдиний час, коли це має сенс, це якщо імена невідомі в кодовий час (тобто вони надходять із бази даних або зовнішнього джерела). Також для складних комбінацій аргументів чудово працюють типи об'єднань. Якщо ви кодуєте конкретні імена, їх слід визначити, якщо це можливо. Якщо це лише дані, які не впливають на логіку, то, звичайно, залишайте їх поза системою типів.
Rick Love

1
@RickLove "імена невідомі в кодовий час" -> це хороший і практичний приклад, значення цих типів клавіш відомі, і всі вони однакові (наприклад, рядок ). Майте на увазі, що я виступав лише за використання [key: string]частини, а не будь-якої частини як визначення типу значення ... Це дійсно вивело б корисність типів.
CPHPython

1
Як я можу йти, якщо хочу дозволити додавання рядків і чисел, але не інших типів?
DonkeyBanana

14

Якщо ви намагаєтесь написати анотацію типу, синтаксис є:

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

Якщо ви намагаєтесь написати об'єкт буквально, синтаксис:

var x = { property: 'hello' };

Ваш код намагається використовувати ім'я типу у позиції значення.


10
Твоя var x: { property: string; } = ...;дражниця! Я сподівався, що еліпсис є коректним синтаксисом var x: { property: string; } = { property: 'hello' };.
Джейсон Клебан

10

У TypeScript, якщо ми оголошуємо об'єкт, тоді ми використовуємо наступний синтаксис:

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

Наприклад:

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

7

Якщо ви намагаєтеся додати типізації до деструктурованого літералу об'єкта, наприклад, в аргументи функції, синтаксис:

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

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

5

Якщо ваші властивості мають однаковий тип, ви можете використовувати попередньо визначений тип утиліти Record:

type Keys = "property" | "property2"

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

Звичайно, ви можете піти далі та визначити тип власності для своїх властивостей:

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
Саме те, що мені було потрібно. Дякую! Машинопис на виграш!
Аудацит

Я б тут також використовував letзамість varцього.
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
Привіт, ласкаво просимо до SO! Чи можете ви взагалі розширити свою відповідь, було б корисно пояснити, як / чому це працює.
вечірка-дзвінок

1

У вашому коді:

var obj = {
  myProp: string;
};

Ви фактично створюєте об'єкт буквально і присвоюєте змінну рядок властивості myProp. Хоча це дуже погана практика, це насправді дійсний код TS (не використовуйте це!):

var string = 'A string';

var obj = {
  property: string
};

Однак те, що ви хочете, - це те, що вводиться літеральний об'єкт. Цього можна досягти різними способами:

Інтерфейс:

interface myObj {
    property: string;
}

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

Спеціальний тип:

type myObjType = {
    property: string
};

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

У ролях оператора:

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

Тип об’єкта буквальний:

var obj: { property: string; } = { property: "Mystring" };
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.