Обеспечивает ли тип индексируемых членов объекта Typescript?

Я хотел бы сохранить отображение строки -> string в объекте Typescript и принудительно следите за тем, чтобы все ключи отображались на строки. Например:

var stuff = {};
stuff["a"] = "foo";   // okay
stuff["b"] = "bar";   // okay
stuff["c"] = false;   // ERROR!  bool != string

Есть ли способ для меня, чтобы обеспечить, что значения должны быть строками (или любой другой тип ..)?

Ответы на вопрос(6)

@ Райан Каванос ответом полностью в порядке и все еще действителен. стоит добавить, что с осениКогда мы можем утверждать, что ES6 поддерживается большинством платформ, почти всегда лучше придерживаться карты всякий раз, когда вам нужно связать некоторые данные с каким-либо ключом.

Когда мы пишемlet a: { [s: string]: string; } мы должны помнить, что после того, как там написана машинописьэто не такая вещь, как тип данных, этоИспользуется только для компиляции. И {[s: строка]: строка; } скомпилируется как раз {}.

Тем не менее, даже если вынапишу что-то вроде:

class TrickyKey  {}

let dict: {[key:TrickyKey]: string} = {}

Это только что выигралкомпилировать (даже дляtarget es6, вы'получуerror TS1023: An index signature parameter type must be 'string' or 'number'.

Так что практически вы ограничены строкой или числом в качестве потенциального ключа, так чтоЗдесь нет особой необходимости проводить проверку типов, особенно учитывая, что когда js пытается получить доступ к ключу по номеру, он преобразует его в строку.

Поэтому вполне безопасно предположить, что наилучшей практикой является использование Map, даже если ключи строковые, поэтому ябуду придерживаться:

let staff: Map<string, string=""> = new Map();
</string,>
 Roman Starkov27 нояб. 2017 г., 15:09
Не уверен, что это было возможно, когда этот ответ был опубликован, но сегодня вы можете сделать это:let dict: {[key in TrickyKey]: string} = {} - гдеTrickyKey тип строкового литерала (например,"Foo" | "Bar").
 coding02 янв. 2018 г., 18:14
Лично мне нравится нативный формат машинописи, но тыПравильнее всего использовать стандарт. Это дает мне возможность документировать ключ "название" который нене очень удобно, но я могу сделать ключ под названием что-то вродеpatientId» :)

Определить интерфейс

interface Settings {
  lang: 'en' | 'da';
  welcome: boolean;
}

Использовать ключ, чтобы быть определенным ключом интерфейса настроек

private setSettings(key: keyof Settings, value: any) {
   // Update settings key
}
Решение Вопроса
var stuff: { [s: string]: string; } = {};
stuff['a'] = ''; // ok
stuff['a'] = 4;  // error

// ... or, if you're using this a lot and don't want to type so much ...
interface StringMap { [s: string]: string; }
var stuff2: StringMap = { };
// same as above
 Ryan Cavanaugh17 мар. 2017 г., 16:58
Ну, родной тип карты называетсяMapтак что какой-то другой тип должен быть чем-то другим. Конечно, вы можете называть это как хотите.
 Leon27 дек. 2017 г., 08:01
есть ли официальный документ об этом синтаксисе?
 tep13 мар. 2018 г., 18:41
Будь осторожен с{ number: string }потому что, хотя это может обеспечить тип индекса при назначении, объект все еще хранит ключ какstring внутренне. Это может на самом деле запутать TypeScript и нарушить безопасность типов. Например, если вы пытаетесь преобразовать{ number: string } к{ string: number } меняя ключи значениями, вы на самом деле получаете{ string: string } пока TypeScript не делаетвыкидывать любые предупреждения / ошибки
 Armentage12 нояб. 2012 г., 16:46
Какие'Интересно, что синтаксис любого типа, кроместрока» потому что ключ на самом деле не так. Имеет смысл, учитывая, что карты JS явно обозначены строками, но это делает синтаксис несколько избыточным. Что-то вроде '{}: строка ' простое указание типов значений может показаться более простым, если только они не будут добавлены каким-либо образом, чтобы разрешить автоматическое приведение ключей в действие как часть сгенерированного кода.
 Gilad04 авг. 2017 г., 10:20
Не могли бы вы подробнее рассказать об этом синтаксисе? Спасибо.
 amwinter09 мар. 2015 г., 20:15
Стоит отметить: компилятор обеспечивает только тип значения, а не тип ключа. Вы можете делать вещи [15] = 'без разницы' и это победилоне жалуюсь
 Ryan Cavanaugh12 нояб. 2012 г., 18:15
number также допускается как тип индексации
 calebboyd05 июл. 2016 г., 19:48
@RyanCavanaugh Возможно ли (или будет) использоватьtype Keys = 'Acceptable' | 'String' | 'Keys' как тип индексации (ключа)?
 mblakesley07 февр. 2019 г., 02:54
Спасибо за добавленноеinterface пример! Это'это гораздо проще и понятнее, чем пример документа TS, который имеет дополнительную сложность в функции.
 AlexG06 апр. 2016 г., 10:01
Нет, он устанавливает тип ключа. Вы можете'делать вещи [myObject] = 'без разницы' даже если myObject имеет хорошую реализацию toString ().
 Christophe Roussy27 янв. 2015 г., 18:09
Попробуйте ввести значение null, и он скажет: аргумент выражения индекса должен иметь тип 'строка ','число', или же 'любой'.
 SuperUberDuper17 мар. 2017 г., 15:47
Разумно ли называть это StringMap? Не совсем родная карта здесь
 Yakov Fain21 сент. 2017 г., 14:31
Не уверен, что при использовании[key:string] очень помогает Здесь я использую число в качестве ключа, а компилятор нене жалуюсь:typescriptlang.org/play/...
 Sandy Gifford08 мар. 2018 г., 17:31
@YakovFain Я думаю, чтоиз-за странной причуды в Javascript, где1 == "1", Индексаторы также играют по этим правилам -["hello", "world"]["1"] возвращаетсяworld а также({ "1": "foo", "2": "bar" })[2] возвращается.bar

ответ, это позволит применить либо ключ, либо значение - или оба - быть всем, что вы хотите обеспечить.

type IdentifierKeys = 'my.valid.key.1' | 'my.valid.key.2';
type IdentifierValues = 'my.valid.value.1' | 'my.valid.value.2';

let stuff = new Map<identifierkeys, identifiervalues="">();
</identifierkeys,>

Должен также работать с использованиемenum вместоtype определение.

Я ношу этот файл с собой, куда бы я ни пошел

export interface StringTMap<t> { [key: string]: T; };
export interface NumberTMap<t> { [key: number]: T; };

export interface StringAnyMap extends StringTMap<any> {};
export interface NumberAnyMap extends NumberTMap<any> {};

export interface StringStringMap extends StringTMap<string> {};
export interface NumberStringMap extends NumberTMap<string> {};

export interface StringNumberMap extends StringTMap<number> {};
export interface NumberNumberMap extends NumberTMap<number> {};

export interface StringBooleanMap extends StringTMap<boolean> {};
export interface NumberBooleanMap extends NumberTMap<boolean> {};
</boolean></boolean></number></number></string></string></any></any></t></t>

StringTMap а такжеNumberTMap являются универсальными и могут быть использованы для создания карт любого типа (черезlet myTypeMap: StringTMap = {}). Остальные полезные предопределенные отображения между общими литеральными типами.

Важный синтаксис здесь{ [key: string]: T; } что означает, что интерфейс обеспечивает объектный литерал с ключами типаstring (словоkey может быть любым идентификатором и должен использоваться для указания важности ключа) и значений типаT, Если вы хотите построить объект сstring "имена» для ключей иboolean значения (и не использовать наследование выше) это 'с интерфейсом будет.{ [name: string]: boolean }

10.10.2018 обновление: Проверьте @dracstaxi 'ответ ниже - тамтеперь встроенный типRecord который делает более или менее именно это.

 Matt Welke02 апр. 2018 г., 07:38
Может быть хорошим кандидатом на модуль NPM!
 robocat05 февр. 2019 г., 00:54
{[ключ: номер]: T; } не безопасен, потому что внутренне ключи объектавсегда строка - см. комментарий к вопросу от @tep для более подробной информации. например Бегx = {}; x[1] = 2; тогда в ChromeObject.keys(x) возвращает ["1"] а такжеJSON.stringify(x) возвращает '{"1": 2}», Угловые шкафы с typeofNumber (например, Infinity, NaN, 1e300, 999999999999999999999 и т. д.) преобразуются в строковые ключи. Также остерегайтесь других угловых случаев для строковых ключей, таких как,x[''] = 'empty string';x['000'] = 'threezeros'; ,x[undefined] = 'foo'

Record это действует как словарь.

Пример из документации:

// For every properties K of type T, transform it to U
function mapObject<k extends="" string,="" t,="" u="">(obj: Record<k, t="">, f: (x: T) => U): Record<k, u="">

const names = { foo: "hello", bar: "world", baz: "bye" };
const lengths = mapObject(names, s => s.length);  // { foo: number, bar: number, baz: number }
</k,></k,></k>

Документация по TypeScript 2.1Record

Единственный недостаток, который я вижу в использовании этого более{[key: T]:K является то, что вы можете закодировать полезную информацию о том, какой ключ вы используете вместо "ключ» например если бы у вашего объекта были только простые ключи, вы могли бы намекнуть на это так:.{[prime: number]: yourType}

Вот'Это регулярное выражение, которое я написал, чтобы помочь с этими преобразованиями. Это преобразует только те случаи, когда меткаключ», Чтобы преобразовать другие метки, просто измените первую группу захвата:

Найти:\{\s*\[(key)\s*(+\s*:\s*(\w+)\s*\]\s*:\s*([^\}]+?)\s*;?\s*\}

Заменить:Record<$2, $3>

 Sandy Gifford10 окт. 2018 г., 18:52
Это должно получить больше голосов - этоПо сути, более новая, нативная версия моего ответа.
 Douglas Gaskell28 мар. 2019 г., 07:54
Компилируется ли запись в?{}

Ваш ответ на вопрос