Задать вопрос
Ответы пользователя по тегу TypeScript
  • TypeScript ругается на getAttribute('href'). Как получить href ссылки?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Ради ускорения сборки из коробки не предусмотрено сложной типизации событий. Одно время события имели сложную типизацию на дженериках, но оную вкинули из ts. (Что весьма меня расстраивает, могли бы и оставить как отдельную настройку, для тех кому плавная типизация важнее скорости.)

    Тебе остаётся только кастовать типы руками, увы. Причём рекомендую в данном случае использовать currentTarget а не target, потому что target может оказаться например span внутри этой ссылки, а не она сама.
    Ответ написан
  • Типизировать src,The expected type comes from property 'src' which is declared here on type?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Ответ на твой вопрос на самом деле зависит от того, что за говно у тебя в cartEmptyImg.
    Т.к. голый js не умеет в принципе импортировать что-то кроме js модулей - это полностью зависит от твоей конфигурации загрузчиков и настроек, о которых в вопросе не сказано ни слова.
    Даже хотя-бы полный текст ошибки частично пролил бы свет на этот вопрос, но конечно же "лишняя" информация в вопросе не нужна, да.

    В целом тут может быть несколько вариантов:
    1. Используется простой загрузчик, который в импорт подставит простой url или data uri, тогда у тебя хреново настроен тип для png: должен быть где-то файлик условно вида:
    png.d.ts
    declare module '*.png' {
      const png: string;
      export default png;
    }

    2. Используется какая-то более продвинутая хрень(генератор спрайтов, генератор react-копонентов, и т.д.). Тут уж сам смотри что там и куда это что-то надо пихать, потому как без инфы тут можно только гадать на кофейной гуще.
    Ответ написан
  • Как конкретизировать сложный тип?

    Aetae
    @Aetae Автор вопроса, куратор тега TypeScript
    Тлен
    И вот в ts 4.9 наконец-то ввели фичу отвечающую на этот вопрос (буквально джва года ждал):
    interface Some {
      value: string | string[];
    }
    
    const foo = {
      value: ['a', 'b'] // здесь всегда будет массив
    } satisfies Some;
    
    foo.value.map(() => {}); // ок
    Ответ написан
    Комментировать
  • Как правильно типизировать аругменты?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    const someFunc = <
      FK extends keyof FormDataType, 
      SK extends keyof FormDataType[FK]
    >(
    	firstKey: FK, 
    	secondKey: SK
    ) => {
      ...
    }
    Ответ написан
    Комментировать
  • Как подсветить все ошибки typescript в webstorm?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    На самом деле твой вопрос: "Как подсветить ошибки типов typescript внутри шаблонов vue в webstorm".

    Ответ: никак.

    Они завязали всё на стороннюю тулзу и плагин к vscode, а не на языковой сервис typescript, потому любые ide кроме vscode в пролёте. Это очень расстраивает, да.

    У меня есть личные намётки на эту тему, но оно движется медленно и постоянно откладывается, т.к. есть задачи по-актуальней, так что в ближайшее время можно не ждать.)

    P.S. Могу пока порекомендовать плагин awesome console - он сделает кликабеьными все ссылки в консоли, включая переход к конкретной строчке.
    Ответ написан
    1 комментарий
  • Как указать компилятору скопировать .json схемы?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Если ты не используешь никаких систем сборки, а только чистый tsc - то оный отвечает только и исключительно за транспиляцию самого ts, всё остальное тебе придётся делать тупо ручками: написать bash\bat\nodejs скрипт, который последовательно запустит tsc, потом запустит твой "пакет", затем просто скопирует нужные файлы в папочку dist.

    Если используешь - гугли соответствующие плагины для конкретной системы, либо пиши свои(это проще чем кажется). Например для webpack есть copy-webpack-plugin.
    Ответ написан
    1 комментарий
  • Как типизировать в ts?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Вопрос конечно в духе "я его того, а он мне нихрна, чаво это он". Где конкретика блин? Что именно за ошибка? Любая ошибка TS - это "ошибка типов", он для этого и существует. Что у тебя в route?

    Но окей, предположим, что route у тебя - это текущий роут vue-рутера, тогда ошибка у тебя скорее всего выглядит как-то так:
    TS7053: Element implicitly has an 'any' type because expression of type 'RouteRecordName' can't be used to index type '{ first: string; second: string; }'.   No index signature with a parameter of type 'string' was found on type '{ first: string; second: string; }'.

    Собственно прочитав текст ошибки можно уже догадаться, в чём проблема: у (неявно выведенного за отсутствием явного объявления) типа объекта pages есть ключи типа 'first' | 'second' | ..., но нет index signature типа string, т.е. не указано, что ключом может быть любая строка, а не только конкретные'first' | 'second' | ....
    route.name же после проверки на пустоту имеет тип string | symbol. Ты не можешь у объекта с чётко ограниченным набором ключей брать значение по произвольному строковому/символьному.

    Прямое решение в лоб:
    Задать тип pages позволяющий рандомные ключи, например
    const pages: Record<PropertyKey, string> = {
      first: 'Первая',
      second: 'Вторая',
      ...
    }

    Всё сразу заработает, но это не спасёт тебя от ошибок(например опечаток).


    Не менее прямой вариант
    (но с другой стороны):
    Кастануть нужный тип руками: ... = pages[route.name as keyof typeof pages]
    Ведь мы уверены, что name в route всегда будет одним из ключей pages. Уверены же?..

    Энтерпрайз решение(ничем не лучше предыдущих, зато выглядит солидно):
    Твой файл routes.ts:
    export enum ERoutes {
      FIRST = 'first',
      SECOND = 'second'
    }
    
    const routes = [
      {
         name: ERoutes.FIRST,
         ...
      },
      {
         name: ERoutes.SECOND,
         ...
      },
      ...
    ]

    В коде:
    const pages: Record<ERoutes, string> = {
      [ERoutes.FIRST]: 'Первая',
      [ERoutes.SECOND]: 'Вторая',
      ...
    }
    
    ... = pages[route.name as ERoutes]


    Надмозговое решение("как батька"):
    Твой файл routes.ts:
    const routes = [
      {
         name: 'first',
         ...
      },
      {
         name: 'second',
         ...
      },
      ...
    ] as const satisfies ReadonlyArray<ReadonlyRouteRecordRaw>;
    
    type ReadonlyRouteRecordRaw = Omit<RouteRecordRaw, 'children'> & {
      children?: ReadonlyArray<ReadonlyRouteRecordRaw>;
    };
    
    type ExtractNames<Route> = Route extends { name: infer Name } ? Name : never;
    type FlattenChildren<Route> = Route extends { children: ReadonlyArray<infer Children> }
      ? FlattenChildren<Children> | Route
      : Route;
    
    // с помощью магии ts вытаскиваем в тип RouteNames все заданные у нас имена маршрутов
    export type RouteNames = ExtractNames<FlattenChildren<typeof routes[number]>>;
    
    // с помощью магии же прокидывем их прямо в декларацию vue-router
    declare module 'vue-router' {
      export interface RouteLocationNormalizedLoaded {
        name: RouteNames | null | undefined;
      }
    }

    satisfies
    satisfies - новая фича ts 4.9, в предыдущих версиях того же можно добиться сделав обёртку вида:
    const narrowRoutesTypeWrapper = <T extends ReadonlyArray<ReadonlyRouteRecordRaw>>(routes: T) => routes;
    const routes = narrowRoutesTypeWrapper([ ... ] as const);

    И твой код заработает вообще без изменений (если в pages есть все нужные ключи).)
    Однако для удобства можно написать так:
    const pages: Record<RouteNames, string> = {
      first: 'Первая',
      second: 'Вторая',
      ...
    }
    Ответ написан
    Комментировать
  • Как компилировать ts в js используя cdn?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Есть какая-то такая хрень:
    https://www.npmjs.com/package/ts-browser-klesun
    https://www.npmjs.com/package/ts-browser

    Но правильный ответ: на проде - никак. Компиляция ts это жирнющий процесс, который будет работать очень медленно.
    Ответ написан
    Комментировать
  • Для чего нужен параметр sourceMap в tsconfig?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    webpack - это webpack, typescript - это typescript, всекаешь?

    Если нет, то поясню: это разные инструменты, которые могут прекрасно работать сами по себе и никак не пересекаться. Но в обоих случаях есть необходимость sourceMap.
    Если говорить о связке webpack и typescript, при работе в лоб webpack, как сборщик, потребляет sourceMap из typescript и генерирует из них свои.

    Сгенерированные из webpack sourceMap при отключении оных в typescript могут иметь разный вид в зависимости от настроек, плагинов и режимов: от нормальных, т.к. об этом позаботились плагины или вообще компиляция шла через babel, до полностью сломанных и бесполезных.
    Ответ написан
    Комментировать
  • Как сделать продвинутый типизированный Generic для компонента React таблицы?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Надо явно создать union:
    type ITableColumn<
      DataInterface extends object, 
      Keys extends keyof DataInterface = keyof DataInterface
    > = {
      [K in Keys]: {
        key: K;
        title: React.ReactNode;
        hasSort?: boolean;
        render?: (item: DataInterface[K]) => React.ReactNode;
      }
    }[Keys];
    Ответ написан
    4 комментария
  • Как правильно определить тип функции и события?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Мужик. Ты спик инглиш или как? MouseEventHandler переводится как "дрессировщик мышей для эвента" "обработчик событий мыши". KeyboardEventHandler как что? Правильно, "обработчик событий клавиатуры".
    Давайте подумаем, что же функция с такими типам может ожидать на вход, а? Может, соответственно, события мыши или клавиатуры(MouseEvent/KeyboardEvent)? Да не, бред какой-то.

    Но окей, дальше мы назначили пропсам интерфейс, где getCity таки имеет такой тип. И куда же мы передаём этот getCity? А передаём мы его в onSubmit. Скажи же мне, друже, submit - это событие клавиатуры? Или может быть это событие мыши? Ты уверен? Вот и мне кажется что нет.
    Ответ написан
  • Как использовать useSlots().default() vue3 внутри script setup?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    о какой рендер функции идет речь?

    render-function

    Это более низкоуровневый вариант работы с vue, вместо использования <template>.(под капотом <template> компилируется в render-функцию)

    Render-функцию можно возвращать из setup, можно класть в свойство render.
    При script setup, можно сделать так:
    <template>
      <div>
        <render/>
      </div>
    </template>
    
    <script setup lang="ts">
      import { h, useSlots } from 'vue'
    
      const slots = useSlots();
      const render = () => {
        return h('div', slots.default());
      };
    </script>

    Но не нужно в подавляющем большинстве случаев. Мешать всё в одну кучу - весьма опасно.
    Ответ написан
    Комментировать
  • Как улучшить качество камеры в агуляр пва?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    1. Typescript тут не при чём.
    2. Если ты используешь метод из "camera-hardware", то проблема очевидна. Если из "camera-software", то хз, должно работать.
    Ответ написан
    Комментировать
  • Как грамотно настроить алиасы импортов?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Ну собсно по хардкору:
    1. Invalidate Caches если ещё не сделал.
    2. Закрыть, удалить папку .idea, открыть чтоб распарсил заново.
    3. Создать новый проект с заведомо рабочим конфигом и перекидывать туда кусками пока не сломается и так найти виновника.
    Ответ написан
  • Как дополнить библиотеку RxJs функцией update() для запуска next()?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Трогать родной BehaviorSubject не стоит, не одному тебе потом с ним работать.

    Ты мог бы написать свой вариант довольно просто таким образом:
    class DistinctBehaviorSubject<T> extends BehaviorSubject<T> {
      next(value: T) {
        if (this.value !== value)
        	super.next(value);
      }
    }
    Но я на вскидку не скажу какие тут могут быть подводные камни.)
    Ответ написан
    1 комментарий
  • Как создать декоратор на typescript для слежения за изменениями?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    На вскидку не вижу проблемы, что-то такое должно сработать:
    function doSomething(param: string, value: unknown) {
      console.log('doSomething', param, value);
    }
    
    function WebworkerParam(param: string){
      return (target: any, key: PropertyKey) => {
        let subscription: Subscription;
        let innerValue: any;
    
        Object.defineProperty(target, key, {
          get() {
            return innerValue;
          },
          set(value) {
            if (subscription)
              subscription.unsubscribe();
    
            if(value instanceof BehaviorSubject)
              subscription = value.subscribe(newValue => doSomething(param, newValue));
            else 
              doSomething(param, value);
    
            innerValue = value; 
          }   
        })
      }
    }
    Ответ написан
    1 комментарий
  • Можно ли в typescript создать переменную определённого типа динамически, имея параметр строчного типа?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    class User {}
    
    interface Types {
      number: number;
      dog: string; 
      user: User
    }
    
    function add<K extends keyof Types>(type: K): Types[K] {}
    Ответ написан
    Комментировать
  • Как типизировать класс с динамическими именами методов создаваемых в конструкторе?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    TypeScript очень плохо относится к динамическим членам класса. По сути в самой декларации класса ты ничего не сможешь с этим поделать, и тебе придётся руками кастовать то что надо.

    Однако в целях поддержки лекаси ты можешь просто подменить тип, что-то вроде:
    type MethodNames<T extends string> = `get${T}s` | `create${T}` | `get${T}`;
    
    interface IAPI {
      new <T extends string>(type: T, url: string): {
        [K in MethodNames<T>]: Function
      }
    }
    
    export default API as unknown as IAPI;
    сработает как надо. Естественно ты можешь доработать интерфейс IAPI до полного совпадения.

    При декларации самого класса ты максимум можешь добавить неспецифичную index signuture, условно:
    type MethodNames<T extends string> = `get${T}s` | `create${T}` | `get${T}`;
    class API {
      [key: MethodNames<string>]: Function
    
      _url;
    
      constructor(type: string, url: string) {
        this._url = url;
    
        this[`get${type}s`] = this._readMany;
        this[`create${type}`] = this._create;
        this[`get${type}`] = this._read;
      }
    
      _readMany(params = {}) {
        const options = getApiOptions(Method.GET);
        const url = new URL(this._url);
        url.search = new URLSearchParams(params);
        
        return fetch(url.toString(), options);
      }
    
      _create(body) {
        const options = getApiOptions(Method.POST, body);
        
        return fetch(this._url, options);
      }
    
      _read(id) {
        const options = getApiOptions(Method.GET);
        
        return fetch(`${this._url}/{id}`, options);
      }
    }

    Но это уже будет не так удобно и чётко.
    Ответ написан
    4 комментария
  • Как исправить ошибку Element' is not assignable to type в React?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Ну очевидно, что NavLink не принимает функцию в качестве children. Могу предположить, что код у тебя для одной версии роутера, а используется другая.
    Ответ написан
    5 комментариев
  • Как вывести тип данных Data в заголовок?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    У тебя две совершенно разные проблемы в одном вопросе:

    1. TS тебе пишет ошибку, что тип Date не может быть children'ом для компонента, и это так. Однако дело в том, что тип у тебя задан неверно, на самом деле у тебя там никакой не Date а банальный string. Если ты поправишь типы, то всё заработает и выведет тебе то что ты передаёшь, т.е. 2012-03-23T08:25:44.962Z.

    2. Чтоб превратить строку 2012-03-23T08:25:44.962Z в строку Created 7 years ago надо либо руками написать соответствующую функцию, либо воспользоваться какой-либо библиотекой для работы с датами. Например с помощью moment это будет выглядеть примерно так:
    const createdFromNow = `Created ${
      moment.duration(moment().diff(data.createdAt)).humanize()
    } ago`;
    Ответ написан
    Комментировать