Typescript: Быстрое исправление для «объединения нельзя использовать в индексных подписях, вместо этого используйте сопоставленный тип объекта»

Созданный на 17 мая 2018  ·  37Комментарии  ·  Источник: microsoft/TypeScript

Следующий код:

type K = "foo" | "bar";

interface SomeType {
    [prop: K]: any;
}

Выдает это сообщение об ошибке:

An index signature parameter type cannot be a union type. Consider using a mapped object type instead.

Никто не знает, что такое сопоставленные типы объектов, поэтому давайте быстро исправим

  • Переключает подпись индекса на сопоставленный тип
  • Перемещает другие элементы в отдельный тип объекта, который комбинируется с типом пересечения
  • Изменяет тип содержащего объекта на псевдоним типа, если тип содержащего объекта является интерфейсом
  • Пересекается со всеми предложениями extends если содержащий тип объекта является интерфейсом и имеет любые предложения extends
Error Messages Quick Fixes Moderate Fixed Suggestion help wanted

Самый полезный комментарий

Ты можешь это сделать:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Хотя Bar не имеет подписи индекса (т. Е. Вы не можете выполнить (obj as Bar)[value as Foo] ).

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

Все 37 Комментарий

Никто не знает, что такое сопоставленные типы объектов, поэтому давайте быстро исправим

+1, просто пришел сюда, потому что я ожидал, что 2.9 будет поддерживать союзы в качестве индексных подписей для вашего примера кода. Я думаю, что это давно желанная функция: # 5683, # 16760 и т. Д.

Ты можешь это сделать:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Хотя Bar не имеет подписи индекса (т. Е. Вы не можете выполнить (obj as Bar)[value as Foo] ).

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

я бы хотел поработать над этим: смеется:

Перемещает другие элементы в отдельный тип объекта, который комбинируется с типом пересечения

что делать, если объектный тип содержит класс?
Могу только представить, что это интерфейс

так что должен делать код после быстрого исправления?

type K = "1" | "2"

class SomeType {
    a = 1;
    [prop: K]: any;
}

так что должен делать код после быстрого исправления?

Я бы сказал, это поправимо ..

@mhegazy Я использую 3.0.0-rc и все еще получаю ту же ошибку, что и изначально. Ожидается ли это?

Я использую 3.0.0-rc и по-прежнему получаю ту же ошибку, что и изначально. Ожидается ли это?

да. ошибка правильная. эта проблема отслеживалась добавлением быстрого исправления для нее, то есть светлой мякоти рядом с сообщением об ошибке.

в версиях 2.9.1 и vscode отсутствуют действия кода

@ThaJay Мы не будем обратно , попробуйте установить более новую версию.

Очевидно. Прошу прощения за то, что сначала не проверил график, просто предположил, что он достаточно новый. Новое в ts. Проверю с версией 3.

как это описать:

function createRequestTypes(base){
  return ['REQUEST', 'SUCCESS', 'FAILURE'].reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, {})
}

const user = createRequestTypes('USER')
console.log(user.REQUEST) // error
// just string? like:
interface IRequestType: {[key: string]: string}

Я пробовал ниже, все не удалось:

type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = {
  [key in requestStatus]: string
}
// or
interface IRequestTypes {[key: keyType]: string}
// or even
type requestTypes = {
  FAILURE: string,
  SUCCESS: string,
  REQUEST: string
}

@maicWorkGithub, вот и все:

const user = createRequestTypes('USER')
console.log(user.REQUEST) 

function createRequestTypes(base:string):requestTypes {
  const result : requestTypes    = {}
  const arr    : requestStatus[] = ['REQUEST', 'SUCCESS', 'FAILURE']  

  return arr.reduce((acc, type) => {
    acc[type] = `${base}_${type}`
    return acc
  }, result)
}


type requestStatus = 'REQUEST' | 'SUCCESS' | 'FAILURE'
type requestTypes = { [key in requestStatus]?: string }

@ihorskyi Спасибо !!

Просто любопытно, почему type работает, а interface - нет. Кто-нибудь может объяснить, пожалуйста? В чем причина такого ограничения (или функции?) interface .

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any}; // ok
interface Baz {[key in Foo]: any} // =>

// A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1169)
// A computed property name must be of type 'string', 'number', 'symbol', or 'any'.ts(2464)
// 'Foo' only refers to a type, but is being used as a value here.ts(2693)

Это было удивительное автоматическое исправление. Спасибо за то, что реализовали! :)

То же и для классов.

Ты можешь это сделать:

type Foo = 'a' | 'b';
type Bar = {[key in Foo]: any};

Хотя Bar не имеет подписи индекса (т. Е. Вы не можете выполнить (obj as Bar)[value as Foo] ).

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

Вместо этого используйте Record !

type Foo = 'a' | 'b'
type Bar = Record<Foo, any>

Чтобы добавить еще один пример этого с использованием класса ...

class Foo {
   a: string;
   b: string;
}

type Bar = {[key in keyof Foo]: any};

еще лучше при использовании частичного

type A = 'x' | 'y' | 'z';
type M = Partial<{
    [key in A]: boolean
}>;

Никто не знает, что такое сопоставленные типы объектов, поэтому давайте быстро исправим

@DanielRosenwasser
Почему сообщение об ошибке не может предложить ответ, например, покажите быстрый пример с использованием сопоставленного типа - это будет всего пара строк кода, который будет соответствовать средней длине сообщений об ошибках TypeScript: trollface:

Кто-нибудь знает, можно ли сказать, что интерфейс, который использует тип или перечисление в качестве ключа, может принимать только одно свойство?

Например подпись вроде этой: { <field>: { <and|or|xor>: <int> } } взято из

export enum BitwiseOperator {
    and = "and",
    or = "or",
    xor = "xor",
}

export type BitwiseCondition = {
    [key in BitwiseOperator]?: number;
}

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

const query: BitwiseCondition = {
  and: 5,
  or: 6  // raise a ts error
};

@ b4dnewz Вы не можете сделать это в Typescript. Обходной путь: https://github.com/Microsoft/TypeScript/issues/10575

@ b4dnewz , если вам нужен только 1 объект, почему бы не сделать это так?

export enum BitwiseOperator {
  and = "and",
  or = "or",
  xor = "xor",
}

export type BitwiseCondition = {
  operator: BitwiseOperator;
  value: number;
}

@benwinding, к сожалению, возвращенная форма отличается от того, что ожидает mongodb

@apieceofbart спасибо за предложение, я изучил его, немного избыточно с точки зрения интерфейсов, но может работать, я не уверен, что я его сейчас реализовал, поскольку это не имеет большого значения, если конечный пользователь попробует побитовое условие с двумя операторами, mongo все равно выдаст ошибку

Я стараюсь, чтобы определения монго-операторов были как можно более простыми, чтобы избежать головной боли 😁 возможно, в будущем будет добавлена ​​соответствующая поддержка

@ b4dnewz достаточно честно,

Возможно, вы могли бы использовать более простой вариант:

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

Это самое близкое, что вы получите без особого дублирования

@ b4dnewz достаточно честно,

Возможно, вы могли бы использовать более простой вариант:

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

Это самое близкое, что вы получите без особого дублирования

В этом примере это не приведет к ошибке:

const query: BitwiseCondition = {
  and: 5,
  or: 6  // raise a ts error
};

Я думал в этом весь смысл

@apieceofbart ,

В этом примере это не приведет к ошибке:

export type BitwiseCondition =
  | { or: number }
  | { xor: number }
  | { and: number }

const query: BitwiseCondition = {
  and: 5,
  or: 6  // doesn't raise a ts error!
};

Вау! это странно: open_mouth: я этого не знал!

Похоже, что Typescript не поддерживает взаимоисключающие типы для объектов. Здесь также было предложение по языку: https://github.com/microsoft/TypeScript/issues/14094

Хотя технически это все еще возможно ...

Из этого ответа условных типов (самых сложных типов), но это не очень ...

/*
 XOR boiler plate
*/
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = T | U extends object
  ? (Without<T, U> & U) | (Without<U, T> & T)
  : T | U;
type XOR3<S, T, U> = XOR<S, XOR<T, U>>;

// Code start
export type BitwiseCondition = XOR3<
  { or: number },
  { xor: number },
  { and: number }
>;

const query1: BitwiseCondition = {
  and: 5
};

const query: BitwiseCondition = {
  and: 5,
  or: 6 // raise a ts error
};

Если кто-то может сделать это красивее или лучше, пожалуйста, сделайте

@mvasin FWIW, это _показано_ для достижения того же результата, но я полностью согласен с тем, что это должно быть особенностью интерфейсов, как и для типов.

type Foo = 'a' | 'b';

type Bar = {
  [key in Foo]: any
}

interface A extends Bar { }

class Wol implements A{
  a: any;
  b: any;
}

Для машинописного текста 3.5 кажется, что я должен сделать это:

export interface DataTableState {
  columnStats: {[key in keyof DataTable]?:{}}
}

Это лучший способ сделать это?

Почему именно индексная подпись не может использовать перечисляемый тип? Сопоставленный тип почти делает то, что я хочу, но тогда TypeScript ожидает, что каждая строка из перечисления будет существовать как определенный ключ. На самом деле я не хочу утверждать, что каждый ключ существует, более того, если какие-либо ключи действительно существуют, они должны жить в перечислении.

Например для типа:

type MyType = {
  [Key: 'foo' | 'bar' | 'zip']: number;
};

Это должно удовлетворить:

const x: MyType = {
  foo: 1,
  zip: 2
};

Хотя я мог бы просто установить другие ключи undefined для сопоставленного типа, я предпочитаю сделать ключи необязательными, но если они есть, значение не может быть неопределенным. Если я сделаю значения сопоставленных типов необязательными, код будет работать, но типы будут менее сильными.

еще лучше при использовании частичного

type A = 'x' | 'y' | 'z';
type M = Partial<{
    [key in A]: boolean
}>;

Благодаря!
Полезно, когда вам нужно определить тип, частично соответствующий словарю

«Частично» также можно использовать в записях:

type Foo = 'a' | 'b';
let foo1: Record<Foo, number> = { a: 1, b: 2 };
let foo2: Partial<Record<Foo, number>> = { a: 1 };

Я случайно захожу на эту страницу GitHub примерно раз в месяц.

Мой последний очень простой:

interface ObjectLiteral {
    [key: string | number]: any
}
export const mapToObjectLiteral = (map: Map<string|number, any>) =>
    Array.from(map).reduce((objLit, [key, value]) => {
        objLit[key] = value
        return objLit
    }, {} as ObjectLiteral)

image

Я могу прокрутить вверх и найти обходной путь, но просто хотел сообщить, что эта проблема часто возникает при повседневной работе в немного разных сценариях.

вот пример:

type MapKey = string | number;
type ObjectLiteral<T extends MapKey, V = any> = {
  [P in T extends number ? string : T]: V;
};

export const mapToObjectLiteral = <T extends MapKey, V>(map: Map<T, V>) =>
  Array.from(map).reduce((objLit, [key, value]) => {
    objLit[key as keyof ObjectLiteral<T>] = value;
    return objLit;
  }, {} as ObjectLiteral<T, V>);

// how to make a better type of map ?
const m = new Map<1 | "foo", "a" | "b">();
m.set(1, "a");
m.set("foo", "b");

const o = mapToObjectLiteral(new Map(m));

console.log(o[1], o.foo); // just got an union type of every member of 'o'

https://github.com/microsoft/TypeScript/issues/24220#issuecomment -504285702

Чтобы добавить еще один пример этого с использованием класса ...

class Foo {
   a: string;
   b: string;
}

type Bar = {[key in keyof Foo]: any};

Очень полезно. Благодаря! 🚀

Была ли эта страница полезной?
0 / 5 - 0 рейтинги