@ColdSpirit

Как перезаписать поля HTMLElement полями из входного объекта в Typescript?

Добрый день! Цель - создать функцию, которая принимает тег, опции для перезаписи полей, и выдает готовый к пользованию объект. Но тайпскрипт кидает палки в колеса, не пойму почему. С тайпскриптом мало знаком, нагуглил тип, который вытаскивает разрешенные к записи поля у объекта, вроде как он должен работать, но все равно ошибка странная выдается. Как исправить ошибку?

type IfEquals<X, Y, A=X, B=never> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? A : B;

type WritableKeys<T> = {
  [P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T];

type ReadonlyKeys<T> = {
  [P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, never, P>
}[keyof T];

type WritableFields = Pick<HTMLElement, WritableKeys<HTMLElement>>

export function createElement(tag: string, options?: WritableFields): HTMLElement {
    let element = document.createElement(tag)

    if (options) {
        // assign options
        let option: keyof WritableFields
        for (option in options) {
            element[option] = options[option] // Type 'undefined' is not assignable to type 'never'
        }
    }

    return element
}


Тот же пример в песочнице: ссылка

P.S. Про Object.assign знаю, интересует, как решить именно эту задачу.
  • Вопрос задан
  • 167 просмотров
Решения вопроса 1
Aetae
@Aetae Куратор тега TypeScript
Тлен
Яб сделал так:
type WritableFields<T> = Partial<Pick<T, WritableKeys<T>>>;
export function createElement<K extends keyof HTMLElementTagNameMap>(tag: K, options?: WritableFields<HTMLElementTagNameMap[K]>): HTMLElementTagNameMap[K] {
  let element = document.createElement(tag)

  if (options) {
    // assign options
    Object.assign(element, options);
  }

  return element
}


Если нужно именно с перебором, то можно так:
type WritableFields<T> = Partial<Pick<T, WritableKeys<T>>>;
export function createElement<K extends keyof HTMLElementTagNameMap>(tag: K, options?: WritableFields<HTMLElementTagNameMap[K]>): HTMLElementTagNameMap[K] {
  let element = document.createElement(tag)

  if (options) {
    // assign options
    let option: keyof typeof options;
    for (option in options) {
      element[option] = options[option]!; // ! - потому что теоретически ты можешь передать undefined явно, но морочиться с этим не стоит
    }
  }

  return element
}


P.S. Вообще все сложности с перебором переданного объекта в typescript происходят из того, что типизация в нём структураная: например имея интерфейс параметра, например,
interface Foo {
  foo: number;
}
тебе ничего не мешает передать ему
{
  foo: 1,
  bar: 2
}
, т.к. данный объект расширяет интерфейс Foo и, соответственно, удовлетворяет ему. Получается, что внутри функции typescript никак не может быть уверен, что ключи указанные в интерфейсе - это ВСЕ ключи, а значит не может быть уверен, что при переборе и присвоении не будет присвоено что-то лишнее. (Это не говоря уже о прототипах, геттерах и прочем)
Потому и требуется куча плясок, увы.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы