Ответы пользователя по тегу TypeScript
  • Что такое design:type в рамках метаданных в TypeScript?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Это то что генерирует TypeScript при включенном параметре emitDecoratorMetadata
    https://www.typescriptlang.org/docs/handbook/decor...

    Режим зануды

    1.Декораторы - экспериментальная фишка, это значит, что Microsoft всех предупредили, что они все поменяют в любой момент без зазрения совести.
    2. Декораторы в TypeScript очень сильно конфликтуют с пропозолом декораторов в ECMAScript (стандарт JS).
    3. TypeScript следует стандартам ECMAScript.
    4. Однажды настанет день, когда все современные проекты с TypeScript декораторами сломаются, условные Angular или Nest.js думаю это починят в разумные сроки, но большинство же получит по шапке от бизнеса...
    Ответ написан
    5 комментариев
  • Как решить данную ошибку типизации и почему она возникает?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    if (error.value && 'data' in error.value) {
      // ...
    }

    https://www.typescriptlang.org/docs/handbook/2/nar...

    Если кратко, то у error.value тип - юнион, а у юниона можно использовать только свойства, общие для всех вариантов, либо уточнять тип через type guards.
    Ответ написан
    2 комментария
  • Как реализовать DI как в ангуляре?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Интересует @Injectable()

    Эта штука называется "декоратор".
    Декоратор - это по сути просто функция, которая принимает на вход другую функцию или класс или метод класса или свойство класса и может его подменить или модифицировать.
    То есть Injectable - это просто функция, которая возвращает другую функцию, в которую передается ссылка на класс A, и она может сделать с ним что угодно. Например в данном случае она просто запоминает этот класс в DI контейнере.
    Надо учитывать, что декораторы являются экспериментальной фичей TypeScript, их нужно включить в tsconfig, а так же никто не гарантирует, что декораторы всегда будут работать так как работают сейчас, тем более декораторы из TypeScript конфликтуют с декораторами из пропозала в ECMAScript (стандарт JS).
    Подробнее про декораторы можно почитать тут: https://www.typescriptlang.org/docs/handbook/decor...

    Ну а про устройство Dependency Injection лучше нагуглить какую-либо статью и почитать, ибо в рамках ответа не распишешь всех тонкостей.
    Но если вкратце, нам нужен некоторый контейнер, в котором мы отобразим все injectable сущности и их зависимости, а в последствии сможем их оттуда извлекать по запросу, в идеале по типу данных (хотя в TS с этим туго, ибо типы живут только в compiletime), но можно и по классу (которые в TS представлены и типом и runtime значением).
    Ответ написан
  • Что такое signature и implementation?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Сигнатура функции - это по сути ее тип, который состоит из списка аргументов с типами и типа возвращаемого значения.
    Имплементация функции - это функция с телом.
    То есть следующие строки - это сигнатуры:
    function position(): MyPosition
    function position(a:number, b:number): MyPosition
    function position(a:number): MyPositionWithDefault

    Это тоже сигнатура: function position(a?: number, b?:number), но она так же является частью имплементации:
    function position(a?: number, b?:number) {
        if (!a && !b) {
            return {x: undefined, b: undefined}
        }
        if(a && !b) {
            return {x: a, b: undefined, default: a.toString()}
        }
        return {x: a, y: b}
    }


    Перегрузки в TS работают по особенному, не так как в других языках. Все дело в том, что на выходе должен получится JS, в котором никаких перегрузок нет. Поэтому перегрузки работают только на уровне типов.
    Поэтому Вы сначала перечисляете список сигнатур, которые станут типом функции для вызывающей стороны, а потом пишите имплементацию, у которой сигнатура должна быть обобщением всех перегрузок, так как именно она используется для проверки типов внутри функции.

    Еще один важный момент для данного примера - это вывод типов. Если где-то тип не указать, то TS попробует его вывести, но делает он это весьма топорно - из первого значения которое будет присвоено переменной, ну и из первого return если речь идет о выводе возвращаемого значения функции.
    В Вашем случае TS выведет возвращаемое значение имплементации из следующей строчки: return {x: undefined, b: undefined}
    Тип такого значения будет:
    interface ReturnTypeOfPosition {
      x: undefined
      b: undefined
    }
    Проблема тут в двух вещах, во-первых в поле b вместо поля y, во-вторых в отсутствии поля default.
    Если исправить так:
    function position(): MyPosition
    function position(a: number, b: number): MyPosition
    function position(a: number): MyPositionWithDefault
    function position(a?: number, b?: number): MyPosition | MyPositionWithDefault {
        if (!a && !b) {
            return {x: undefined, y: undefined}
        }
        if(a && !b) {
            return {x: a, y: undefined, default: a.toString()}
        }
        return {x: a, y: b}
    }
    то все будет ок

    Ну и еще важный момент про перегрузки, TS использует в месте вызова ту перегрузку, которая первой подошла по аргументам, пробуя их в том порядке, как они записаны у Вас в коде, сверху вниз. Соответственно остальные типы в месте вызова будут выводится из данного факта.

    P.S. у меня в профиле есть ссылка на доклад по системе типов TS, про перегрузки там тоже есть. Система типов в TS полностью построена на математике множеств, если понять эту математику, то поймете и TS.
    Ответ написан
    2 комментария
  • Как правильно типизировать функцию?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    function preparedData<T extends Record<string, {name: string, id: string}[]>>(data: T) {
      return Object.keys(data).map((key) => {
        return data[key as keyof T].map((a) => ({
          val: a.name,
          newId: a.id,
        }));
      });
    }

    https://www.typescriptlang.org/play?#code/GYVwdgxg...
    Ответ написан
    Комментировать
  • Типы сигнатур функций в TypeScript это почти как делегаты в C#?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Функциональные типы в TS - это типы, значением которых может являться любая функция с совместимой сигнатурой. Методы классов - это те же функции. При этом надо помнить, что this в JS/TS определяется вызовом, а не объявлением функции/метода.
    И это существенно отличает их от делегатов в шарпе, которые по своей сути являются умной ссылкой на метод конкретного инстанса.

    Аналог делегата из шарпа на TS может выглядеть как то так:
    class Delegate<Target, Args extends any[], Ret> {
        constructor(
            private target: Target,
            private method: (this: Target, ...args: Args) => Ret,
        ) {}
    
        public invoke(...args: Args): Ret {
            return this.method.apply(this.target, args);
        }
    }
    Вот только конструировать такой делегат в отличии от шарпа придется явно.

    https://www.typescriptlang.org/play?#code/MYGwhgzh...
    Ответ написан
    Комментировать
  • Почему в интерфейс не записывается тип boolean?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    В TypeScript есть литеральные типы ('some string', 1, 2, 3, true, false).
    Значение константы поменять нельзя, поэтому TypeScript выводит для них литеральный тип.
    Поэтому тип q будет не boolean, а true.
    Ответ написан
    4 комментария
  • Почему происходит ошибка типизации computed сomposition api во vue 3?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    А что Вам не понятно в описании текста ошибки?
    Argument of type 'ComputedRef' is not assignable to parameter of type 'string'.

    Давайте я засуну в переводчик за Вас:
    Аргумент типа 'ComputedRef' не может быть присвоен параметру типа 'string'.


    const pErrorMinMessage = computed<string>(() => {
      return 'world'
    })
    const fun1 = function (val: string) {
      console.log('hello' + val)
    }
    
    fun1(pErrorMinMessage.value)
    Ответ написан
    1 комментарий
  • Как типизировать useReducer и есть ли в целом более аккуратные варианты?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    type AlbumType = {
      id: number;
      title: string;
    };
    
    enum ActionPoints {
      SET_ALBUMS = "SET_ALBUMS",
      ADD_ALBUM = "ADD_ALBUM",
      REMOVE_ALBUM = "REMOVE_ALBUM"
    }
    
    type ActionType = {
      type: ActionPoints.SET_ALBUMS;
      payload: AlbumType[];
    } | {
      type: ActionPoints.ADD_ALBUM;
      payload: AlbumType;
    } | {
      type: ActionPoints.REMOVE_ALBUM;
      payload: number;
    };
    
    const reducer = (state: AlbumType[], action: ActionType) => {
      switch (action.type) {
        case ActionPoints.SET_ALBUMS:
          return action.payload;
        case ActionPoints.ADD_ALBUM:
          return [...state, action.payload];
        case ActionPoints.REMOVE_ALBUM:
          return state.filter((album) => album.id !== action.payload);
        default:
          return state;
      }
    };
    Ответ написан
    1 комментарий
  • Нужен ли babel, если используешь TypeScript?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Typescript compiler - это инструмент проверки типов и компиляции typescript в javascript. Так же он умеет транспайлить конструкции новых стандартов js в более старые стандарты, но это не основная задача этого инструмента, и делает он это хуже целевых инструментов, в частности его можно настроить только на конкретный стандарт.

    Babel - это инструмент для парсинга js кода в AST, обхода и модификации AST и обратной сериализации AST в js. Так же это еще и экосистема плагинов и пресетов. Плагины как правило реализуют транспиляцию одной из фич более нового стандарта в более старый. Пресеты - это просто конфигурируемый набор плагинов.
    Так же в Babel AST есть поддержка некоторых расширений js (а ts - это тоже расширение js), но включается это все флагами.

    В экосистеме babel есть preset-env нацеленный именно на транспиляцию более новых стандартов в более старые. Его особенностью является поддержка browserlist и подключения только тех плагинов, которые нужны для указанных браузеров. За счет этого получается более оптимальный js на выходе, так как многие фичи после транспайлинга работают медленнее чем нативные и занимают больше кода.

    Еще в экосистеме babel есть preset-typescript, который включает поддержку ts синтаксиса и транспиляцию ts в js. При этом, в отличии от tsc, он не делает проверку типов, а некоторые сущности может обрабатывать неправильно (как и большинство сторонних компиляторов ts, т.к. у ts нет строгой спеки и единственным источником правды является код tsc, который далек от идеального).

    Оптимальным по выходному результату (пусть и за счет времени сборки) будет вариант, когда сначала ts код обрабатывается через tsc без транспиляции в старые стандарты, а затем происходит транспиляция с помощью babel + preset-env + browserlist.
    Ответ написан
    3 комментария
  • Как указать типы данных в импортируемом json файле?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    https://www.typescriptlang.org/tsconfig#resolveJso...

    Ну или создайте рядом с db.json файл db.json.d.ts со следующим содержимым:
    declare const JSON: {
        id: number;
    }[];
    export default JSON;
    Ответ написан
    Комментировать
  • Как задать тип рекурсивно?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    interface Props {
      list: {
        id: number;
        name: string;
        children?: Props['list'];
      }[];
    }

    или
    interface PropsListItem {
      id: number;
      name: string;
      children?: PropsListItem[];
    }
    
    interface Props {
      list: PropsListItem[];
    }
    Ответ написан
    Комментировать
  • Как задать тип для массива из объектов?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    type T = {
      id: number;
      quantity: number;
    }[];
    Ответ написан
    Комментировать
  • Как из строковых литералов сделать тип массива?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    В TS есть 2 варианта итерироваться по частям юнион типа - это условные типы и итерация по ключам, притом итерация по ключам ограничена типом number | string | symbol

    Условные типы проверяют условие для каждого варианта юниона, а значит позволяют трансформировать каждый из вариантов по отдельности:
    type MyFunc = <N extends string>() => (N extends string ? Field<N> : never)[];
    но возвращаемый тип так же будет юнионом:({ name: "town" } | { name: "city" })[]

    Сделать кортежный тип из юниона не выйдет, так как в юнионе не определен порядок, тип "town" | "city" это тоже самое, что и тип "city" | "town".
    Но можно сделать кортеж из кортежа:
    type MyFunc = <N extends string[]>() => {
        [K in keyof N]: N[K] extends string ? Field<N[K]> : never;
    };
    
    myFunc<["town", "city"]>()


    Ну и в реальном коде гораздо удобнее, когда дженерик выводится из аргументов, а для того что бы TS сам вывел кортежный тип из абстрактного массива, данный массив нужно помечать as const, что так же делает его readonly массивом. Но принимать аргументы через readonly там где нам не нужно их мутирорвать - это вообще хорошая практика. Полный пример будет выглядеть так:
    https://www.typescriptlang.org/play?#code/C4TwDgpg...
    Ответ написан
    Комментировать
  • Как реализуется enum в JS из Typescript?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Можете, пожалуйста, объяснить, что выполняет эта часть кода: (Membership || (Membership = {})
    Если в Membership не falsy значение (например объект), то передать ссылку на него в функцию, иначе (если в Membership undefined) присвоить в Membership новый объект и передать ссылку на него в функцию

    И почему это всё обёрнуто в функцию?
    Сложно сказать, думаю кто-то скопировал код отсюда в код сюда и не стал париться, так как в обоих случаях есть declaration merging, о котором уже написал Aetae

    А вообще стоит узнать про const enum, который не оставляет артефактов в JS и в подавляющем большинстве случаев его достаточно.
    Ответ написан
    Комментировать