Ответы пользователя по тегу TypeScript
  • Типизация массива для функции и смена типа при сравнивании?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    1. Перед return поставьте if(!gif) return null; и станет лучше.)
    Собсно суть логична: вы сами написали что gif может быть undefined, попытка получения свойства у undefined(gif.<some>) - очевидная ошибка.
    Вы должны заранее обработать такой случай и принять меры.

    object в современном ts занчит просто {} без всяких свойств. Если вам нужен любой объект с любыми свойствами, то используйте Record<string, any>. Однако по хорошему никаких any быть не должно, вам следует чётко описать структуру получаемого объекта.

    2. Ничего не понятно. useOnScreen - это нестандартных хук. Реализаций в гугле полно разных. Так что приведите его код и скажите, что вы хотите от него добиться.
    Ответ написан
    7 комментариев
  • Как задать условный дженерик, для входящих в функцию параметров?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Вообще это делается просто так:
    type C = {
        type: string
    }
    
    type Gen = <T extends C | C[]>(a: T) => T
    
    const gen: Gen = (a) => {
        return a
    }

    Если надо что-то более сложное с возвратным значением сделать, то так:
    type Gen = <T extends C | C[]>(a: T) => T extends C[] ? 1 : 2
    Ответ написан
    Комментировать
  • Как настроить глобальный @types в Koa?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Ну не правильно же, ты пытаешься присвоить тип string объекту вида {id: some}.
    const { id }: {id: string}
    Ответ написан
  • Когда исопльзовать Enum, а когда Union?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Юнион когда нужен только тип, енум - когда нужны ещё и рантайм значения. Очевидно же. Константный объект тупо не так удобен как enum, смысла в нём нет для этого класса задач.

    Вообще, строго говоря, enum надо юзать тогда, когда нужен enum. Л - логика. Enum - Enumeration - Перечисление. Есть перечисление - юзай enum.
    Ответ написан
    Комментировать
  • Как создать универсальный условный тип в Typescript?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Только через "палку", для того она и нужна:
    interface CreatureBase {
      type: string;
      subtype: string;
      params: unknown;
    }
    
    interface PeopleBase extends CreatureBase {
      type: 'People';
    }
    
    interface PeopleChild extends PeopleBase {
      subtype: 'child';
      params: {
        age: number;
        school: string;
      }
    }
    
    interface PeopleAdult extends PeopleBase {
      subtype: 'adult';
      params: {
        height: number,
        weight: number,
        age: number
      }
    }
    
    type Creature = PeopleChild | PeopleAdult; //...
    
    const arr: Creature[] = [
      {
        type: 'People',
        subtype: 'adult',
        params: {
          height: 1,
          weight: 2,
          age: 3
        }
      },
      {
        type: 'People',
        subtype: 'child',
        params: {
          school: 'foo',
          age: 3
        }
      },
      {
        type: 'People',
        subtype: 'adult',
        params: {
          school: 'foo', // err
          age: 3
        }
      }
    ]


    А обойти типизацию можно кастанув as any, или дважды кастанув в новый тип var as unknown as type. Но не нужно.)
    Ответ написан
    2 комментария
  • Зачем нужен @typescript-eslint/parser?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Если конфиг унаследован от "plugin:@typescript-eslint/recommended", то не нужен, он просто там уже прописан.

    Суть же в том, что тебе может не понравиться набор правил recommended и ты можешь его выкинуть, набросав свой по вкусу. И тогда всё сломается. Потому лучше указывать парсер сразу явно.
    Ответ написан
    3 комментария
  • Почему reject возвращает тип unknown?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Понавертели лишнего.
    Во-первых: есть хэлперы Promise.reslove(data) и Promise.reject(error), а во-вторых: при возврате из then\catch даже они не нужны.
    interface ResponseData {
      status: boolean;
      data?: object;
    }
    
    function sendQuery(url: string, data: object): Promise<ResponseData> {
      return fetch(url, {
        method: "POST",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json"
        },
        body: JSON.stringify(data)
      }).then(response => 
        response.json()
      ).then(data => ({
        status: true,
        data: data
      })).catch(() => {
        throw {
          status: false
        }
      });
    }


    Хотя ваш вариант НЕ неправильный, просто бессмысленно перегруженный, проблема в том, что TS, увы, не может автоматически вывести тип конструкции new Promise, а потому выдаёт Promise<unknown>. Т.е. каждый раз, когда вы используете new Promise - надо использовать его с дженериком: new Promise<Type>.
    Ответ написан
    Комментировать
  • Как скомпилировать несколько файлов в разных директориях и результат компиляции так же разложить по разным директориям?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Сам tsc же билдит по умолчанию файлы 1 к 1. Это сборщики потом собирают всё воедино. Решение - не использовать сборщик.)
    Если цель только самое современное, можно вообще юзать target esnext, script type="module" и свежую ноду с поддержкой esm.)
    Ответ написан
  • Как исправить ошибку "No index signature..." при обращении к свойствам объекта?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    const queryKey: AuthMessagesKeys = queryObject.message

    Это сработает, но не надёжно - потому что может упасть в рантайме если в message прилетит что-то левое.
    Лучше написать тайпгард, типа
    function isAuthMessagesKey(str: string): str is AuthMessagesKeys {
      return ['login', 'logout', 'session'].includes(str); 
    }

    и делать проверку перед использованием.
    Ответ написан
    Комментировать
  • Как правильно выполнить типизацию кода на React (children и ответ при асинхронном запросе)?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    1. У реакта есть хэлпер PropsWithChildren<Props>. Но под капотом он просто делает так:
    type PropsWithChildren<P> = P & { children?: ReactNode };

    ...upd: вник чутка)
    Если тебе нужен render проп в children, то явно и тапизируй, никаких подвохов:
    type TwitterProps = {
        children: (user: string) => ReactNode
    }

    2. Так и типизировать: const response: ITwitter = .... Только если какая-нить хрень прилетит то может сломаться в рантайме, так что либо ты доверяешь серверу, либо используешь какую-нить либу для дополнительной проверки вживую.)
    Ответ написан
    Комментировать
  • Как перезаписать поля HTMLElement полями из входного объекта в Typescript?

    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 никак не может быть уверен, что ключи указанные в интерфейсе - это ВСЕ ключи, а значит не может быть уверен, что при переборе и присвоении не будет присвоено что-то лишнее. (Это не говоря уже о прототипах, геттерах и прочем)
    Потому и требуется куча плясок, увы.
    Ответ написан
    6 комментариев
  • Почему выдает ошибку Nuxt + TypeScript?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Вы использовали инструкцию или просто рандомно подключили TypeScript к Nuxt?
    Дело в том, что Nuxt много чего подключает неявно, в т.ч. и роутер, и потому TypeScript не знает о том, что в типе Vue появились дополнительные свойства типа $route. Надо отдельно подключать декларации вручную, либо следовать инструкции выше.
    Ответ написан
    Комментировать
  • Почему не распознаётся команда tsc?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Если ты ставил его глобально через npm или yarn - то должно работать сразу.
    Чтобы установленное подтянулось часто надо перезапустить терминал, возможно в этом дело.)
    Ответ написан
    Комментировать
  • Почему нет ошибки при присваивании массива из других интерфейсов?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    В ts структурная типизация. Такое может происходить если Snapshot структурно укладывается в тип Volume.

    Т.е. вот так типы взаимозаменяемы:
    interface Volume {
     id: number;
     volume?: string;
    }
    
    interface Snapshot {
     id: number;
     snapshot?: string;
    }
    А так нет:
    interface Volume {
     id: number;
     volume: string;
    }
    
    interface Snapshot {
     id: number;
     snapshot: string;
    }

    И так нет:
    interface Volume {
     id?: number;
    }
    
    interface Snapshot {
     id?: string;
    }
    Ответ написан
    Комментировать
  • Как затипизировать метод?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Хотелось бы знать, что за тип такой Constants, возможно всё можно сделать проще.
    Но условно - как-то так:
    enum Constants {
      VERTICAL = 'vertical',
      STEP = 'step'
    }
    
    interface ConstantsTypes {
      [Constants.VERTICAL]: boolean,
      [Constants.STEP]: number,
    }
    
    public get<T extends keyof ConstantsTypes>(prop: T): ConstantsTypes[T] {}
    Ответ написан
    Комментировать
  • Почему не работают интерфейсы?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    В обзем шляпа у них с документацией и примерами. Ни в одном примере они не используют объекты вообще, также как нигде нет ни одного примера типизированного объекта. Из чего могу сделать вывод, что с типизированными объектами они не могут работать вообще. Но это не точно.

    Работает например подобная херотень:
    export class IResultItem {
      constructor(
        public id: i32,
        public type: string,
        public date: string,
        public from: string,
        public from_id: i32,
        public text: string
      ) {}
    }
    export function add(
      result: IResultItem[] = [new IResultItem( 1, "type", "25.25.2025", "rew", 123, "hello" )]
    ): IResultItem[] {
    return result;
    }
    Ответ написан
    Комментировать
  • Является ли использование generics с any плохой практикой?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    unknown - совершенно нормально, его можно использовать везде где тип не известен. Он никак не ухудшает качество кода.
    any - совершенно нормально использовать в дженериках, если он применяется для вывода типа (напр. T extends (...args: any) => any). Совсем от него отказываться - охота на ведьм.
    В вашем случае, если тип MyClass в принципе может быть не интересен, то сам MyClass по-хорошему должен быть задан таким образом, чтобы можно было писать просто constructor(private myInstance: MyClass){};, т.е. иметь типы по умолчанию:
    class MyClass<T = unknown> {
         myMethod(): T {...};
    }

    Если же класс не имеет типов по умолчанию, то по логике следует, что дженерик для него важен, и опускать его не стоит. Тут установка unknown или any будет уже плохой практикой. Однако если нет альтернатив, и на дженерик действительно похрен - то что поделать.)
    Ответ написан
    4 комментария
  • Как можно ограничить интерфейс только методами?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Тут понадобится немного магии:
    type ClearIndex<T> = {
      [ P in keyof T as string extends P ? never : P ] : T[P]
    };
    class FOO implements ClearIndex<Service_FOO > {
        methodFoo() {}
    }


    Суть проблемы в том что Record<string, () => void> имеет "index signature", т.е. заявляет, что обращение по любому ключу stringвернёт () => void.
    Service_FOO расширяет этот тип(а не сужает), потому Service_FOO имеет ту же index signature и уточнённый конкретный метод.
    Класс же в ts не может иметь index signature, т.к. является строгой и конкретной структурой.
    Ответ написан
    2 комментария
  • Как решить Parsing Error в ESLint?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    jsx, полагаю, мозги парит. Не юзайте jsx с vue. Хоть это и разрешено, но jsx такое уродливое дерьмо по сравнению с нормальными vue-шаблонами...
    Ответ написан
  • Что может ESLint без плагинов?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    ESlint без плагинов может довести до нервного срыва.
    Ответ написан
    Комментировать