ошибка TS2339: свойство 'x' не существует в типе 'Y'

Я не понимаю, почему этот код генерирует ошибку TypeScript. (Это не оригинальный код и немного выведен, поэтому, пожалуйста, игнорируйте нечувствительность в примере):

interface Images {
  [key:string]: string;
}

function getMainImageUrl(images: Images): string {
  return images.main;
}

Я получаю ошибку (используя TypeScript 1.7.5):

ошибка TS2339: свойство 'main' не существует в типе 'Images'.

Конечно, я мог бы избавиться от ошибки при написании:

return images["main"];

Я бы предпочел не использовать строку для доступа к свойству. Что я могу сделать?

Ответ 1

Если вы хотите иметь доступ к images.main вы должны явно определить его:

interface Images {
    main: string;
    [key:string]: string;
}

function getMainImageUrl(images: Images): string {
    return images.main;
}

Вы не можете получить доступ к индексированным свойствам с помощью точечной нотации, потому что машинопись не имеет способа узнать, имеет ли объект это свойство.
Однако, когда вы конкретно определяете свойство, компилятор знает, что он там (или нет), независимо от того, является ли он необязательным или нет и какой тип.


редактировать

У вас может быть вспомогательный класс для экземпляров карт, например:

class Map<T> {
    private items: { [key: string]: T };

    public constructor() {
        this.items = Object.create(null);
    }

    public set(key: string, value: T): void {
        this.items[key] = value;
    }

    public get(key: string): T {
        return this.items[key];
    }

    public remove(key: string): T {
        let value = this.get(key);
        delete this.items[key];
        return value;
    }
}

function getMainImageUrl(images: Map<string>): string {
    return images.get("main");
}

У меня есть что-то подобное, и я считаю это очень полезным.

Ответ 2

Правильное решение - добавить свойство в определение типа, как описано в ответе @Nitzan Tomer. Если это не вариант, хотя:

(Hacky) Обходной путь 1

Вы можете присвоить объекту константу типа any, а затем вызвать свойство "несуществующий".

const newObj: any = oldObj;
return newObj.someProperty;

Вы также можете разыграть его как any:

return (oldObj as any).someProperty;

Это не может обеспечить безопасность типов, хотя это и есть смысл TypeScript.


(Hacky) Обходной путь 2

Еще одна вещь, которую вы можете рассмотреть, если вы не можете изменить исходный тип, это расширить тип следующим образом:

interface NewType extends OldType {
  someProperty: string;
}

Теперь вы можете привести свою переменную как NewType вместо any. Все еще не идеал, но менее разрешительный, чем any, что обеспечивает вам большую безопасность типов.

return (oldObj as NewType).someProperty;

Ответ 3

Я не эксперт в Typcript, но я думаю, что основная проблема заключается в способе доступа к данным. Посмотрев, как вы описали свой интерфейс " Images, вы можете определить любой ключ как строку.

При доступе к свойству синтаксис "dot" (images.main) предполагает, что он уже существует. У меня были такие проблемы без Typcript, в "ванильном" Javascript, где я пытался получить доступ к данным как:

return json.property[0].index

где индекс был переменной. Но он интерпретировал index, в результате чего:

cannot find property "index" of json.property[0]

И я должен был найти обходное решение, используя ваш синтаксис:

return json.property[0][index]

Это может быть ваш единственный вариант. Но, опять же, я не специалист по машинописному машиностроению, если кто-то знает лучшее решение/объяснение происходящего, не стесняйтесь меня исправлять.

Ответ 4

Допускается использование TypeScript 2.2 с использованием точечной нотации для доступа к индексированным свойствам. Вы не получите ошибку TS2339 на своем примере.

См. Свойство Dotted для типов с индексами строковых индексов в примечании к выпуску TypeScript 2.2.