Ответы пользователя по тегу TypeScript
  • Как задать определенные ключи объекту?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    type TFood = Partial<Record<TFruit, Partial<Record<TFruitProps, string>>>>;
    Ответ написан
    5 комментариев
  • Как решить проблему с Map и async/await "No overload matches this call"?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Задайте явно дженерик для внутреннего Map
    public async getContent() {
    
        const a = new Map([
          [
            'AA',
            new Map<string, string | string[]>([
              ['desc', [await this.i18n.t('Hello'), await this.i18n.t('Hello')]],
              ['a', 'b']
            ])
          ]
        ]);
    Ответ написан
    Комментировать
  • Как работает типизация в данном случае?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
    Оба дженерика имеют типы по умолчанию, а значит если их не указать именно они и будут использоваться
    Когда мы вызываемaxios.post<ResponseApi>(/* ... */)мы явно указываем, что T - это тип ResponseApi, а R берется по умолчанию, то есть AxiosResponse<T>, что в нашем случае соответствует AxiosResponse<ResponseApi>, а если и дальше развернуть, то выходит, что R - это
    {
      data: ResponseApi;
      status: number;
      statusText: string;
      headers: any;
      config: AxiosRequestConfig;
      request?: any;
    }
    он и попадает в возвращаемый тип, обернутый в Promise, но TS знает про поведение await и деструктуризации, а следовательно без проблем вычисляет тип для data - ResponseApi
    Ответ написан
    2 комментария
  • Как создать обертку вокруг HttpClient?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    У реализации должна быть наиболее общая сигнатура от всех перегрузок. Притом типы в реализации работают только внутрь, а наружу торчат только перегрузки:
    export class MyHttpClient {
      constructor(private readonly http: HttpClient) {}
    
      put(params: Params<'body', 'arraybuffer'>): Observable<ArrayBuffer>;
      put(params: Params<'body', 'blob'>): Observable<Blob>;
      put(params: Params<'body', 'text'>): Observable<string>;
      put(params: Params<'events', 'arraybuffer'>): Observable<HttpEvent<ArrayBuffer>>;
      put(params: Params<'events', 'blob'>): Observable<HttpEvent<Blob>>;
      put(params: Params<'events', 'text'>): Observable<HttpEvent<string>>;
      put(params: Params<'events', 'json'>): Observable<HttpEvent<Object>>;
      put<T>(params: Params<'events', 'json'>): Observable<HttpEvent<T>>;
      put(params: Params<'response', 'arraybuffer'>): Observable<HttpResponse<ArrayBuffer>>;
      put(params: Params<'response', 'blob'>): Observable<HttpResponse<Blob>>;
      put(params: Params<'response', 'text'>): Observable<HttpResponse<string>>;
      put(params: Params<'response', 'json'>): Observable<HttpResponse<Object>>;
      put<T>(params: Params<'response', 'json'>): Observable<HttpResponse<T>>;
      put(params: Params<'body', 'json'>): Observable<Object>;
      put(params: Params<Observe, ResponseType>): Observable<unknown> {
        return this.http.put(params.url, params.body, params.options as Params<'body', 'json'>['options']);
      }
    }


    А вообще, мне почему-то кажется, что так будет гораздо проще:
    type ResponseTypeMap = {
      arraybuffer: ArrayBuffer;
      blob: Blob;
      text: string;
      json: Object;
    };
    type ResponseTypes = keyof ResponseTypeMap;
    type ObserveWrapperMap<T extends ResponseTypeMap[ResponseTypes]> = {
      body: Observable<T>;
      events: Observable<HttpEvent<T>>;
      response: Observable<HttpResponse<T>>;
    };
    type ObserveWrappers = keyof ObserveWrapperMap<ResponseTypeMap[ResponseTypes]>;
    type Result<O extends ObserveWrappers, R extends ResponseTypes> = ObserveWrapperMap<ResponseTypeMap[R]>[O];
    type Params<O extends ObserveWrappers, R extends ResponseTypes> = {
      url: string;
      body: any;
      options?: {
        headers?: HttpHeaders | Record<string, string | string[]>;
        observe?: O;
        params?: HttpParams | Record<string, string | string[]>;
        reportProgress?: boolean;
        responseType: R;
        withCredentials?: boolean;
      };
    }
    
    export class MyHttpClient {
      constructor(private readonly http: HttpClient) {}
    
      put<O extends ObserveWrappers = 'body', R extends ResponseTypes = 'json'>(params: Params<O, R>): Result<O, R> {
        const {url, body, options} = params as Params<'body', 'json'>;
        return this.http.put(url, body, options) as Result<O, R>;
      }
    }
    особенно если помимо put будут и другие методы
    Ответ написан
    7 комментариев
  • Как мерджить(обновлять) свойства обьекта с помощью Nestjs?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    function filterObjectProps<O extends Record<string, unknown>, P extends keyof O>(obj: O, props: readonly P[]) {
      const entries: [P, O[P]][] = [];
      for(const prop of props) {
        if(prop in obj) {
          entries.push([prop, obj[prop]]);
        }
      }
      return Object.fromEntries(entries) as {
        [K in P]: O[K];
      };
    }
    
    // ...
    updateItem(id: string, updateItemDto: UpdateItemDto): Item {
      const item = this.getItemById(id);
      return {
        ...item,
        ...filterObjectProps(updateItemDto, ['name', 'lastname', 'prop1', 'prop2'])
      };
    }
    Ответ написан
    1 комментарий
  • Возможно ли описать объект, который может иметь любые ключи?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    type Animal = {
      [key: string]: string;
    };

    Вообще синтаксис такой:
    type Animal = {
      [<любое имя>: <тип ключей>]: <тип значений>;
    };
    Имя должно быть валидным идентификатором, зачем оно нужно TS - я хз, но без него не ест
    тип ключей должен быть подтипом string | number | symbol, притом с symbol есть траблы, там только юнион из конкретных символов
    тип значений - любой тип, но должен не пересекаться с типами явных полей
    явные поля должны быть до универсального

    А вообще в стандартной библиотеке есть утилити тип Record с двумя дженерик аргументами - тип ключей и тип значений, притом у него нет траблов с symbol:type Animal = Record<string, string>;
    Ответ написан
    Комментировать
  • Какой возвращаемый тип у функции, которая возвращает useCallback?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    https://github.com/DefinitelyTyped/DefinitelyTyped...
    Соответственно тип будет:
    (id: number, message: Message) => void
    const useSendMessage = (): (id: number, message: Message) => void => {
      const dispatch = useDispatch();
    
      return useCallback(
        (id: number, message: Message) => {
          dispatch(Operation.sendMessage(id, message));
        },
        [dispatch]
      );
    };
    Ответ написан
    Комментировать
  • Как правильно описать тип в typesctipt?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Все дело в том, что тип profileActionsType - это discriminated union
    Поле post присутствует лишь в одном из его подтипе, соответственно извлечение этого поля из полного типа - с точки зрения typescript - ошибка.
    Проверка на конкретное значение поля type вполне сойдет за type guard, который ts поймет.

    P.S. размещение кода картинками запрещено правилами сервиса
    Ответ написан
  • Как использовать метод static внутри класса через this?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Все прекрасно работает:
    https://www.typescriptlang.org/play?#code/MYGwhgzh...

    Вероятно дело в том, что скобки в вызове метода забыли
    Ответ написан
  • Почему typescript компилятор игнорирует ошибки?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    @babel/preset-typescript не делает тайпчекинг, он просто убирает типы и все.
    Компилируйте тайпскрипт родным компилером, например через ts-loader
    Ответ написан
    1 комментарий
  • Зачем нужны Module и Namespace?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    TypeScript появился еще до того, как в JavaScript, на котором он основан, появились какие либо модули, не то что нативные, даже эмуляций вроде commonjs или amd тогда еще не было. В те времена было в норме просто обернуть содержимое файла в замыкание, а потом либо просто подгрузить все файлы через тэг script либо просто соединить эти файлы в один. Наружу же высвечивалась одна единственная переменная, содержащая все публичное апи такого модуля, ее ложили или в глобальный объект или в другую такую-же переменную.
    Конструкции module и namespace позволяют упростить создание таких переменных, избавив разработчика от написания однотипного кода. В этом плане они по сути делают одно и то же.
    Сейчас это не рекомендованное использование, и стандартный плагин typescript к eslint с настройками по умолчанию запрещает эти конструкции.

    Второе применение - это файлы деклараций.
    namespace позволяет объявить объект, но несколько особенный в рамках типизации, он может содержать в себе любые сущности языка и экспортировать не которые из них, в том числе типы, что недоступно для декларации обычного объектного типа.
    declare namespace XXX {
      // по сути просто поле XXX.a
      let a: number;
      // то же поле XXX.b но уже const
      // современный ts позволяет делать поля readonly, но раньше так было нельзя
      const b: number;
      // функция, по сути метод XXX.c()
      function c(): void;
      // а вот вложенный тип через тип объекта не объявить, а в namespace можно
      type T = number | string;
    }

    module позволяют объявить виртуальные модули, о типах которых typescript не знает, например потому что они генерируются сборщиками вроде webpack. Яркий пример тут это css-модули или картинки, которые можно импортировать благодаря webpack, но typescript ничего не знает о их типах, поэтому нужно объявить их в глобальных декларациях:
    declare module '*.png' {
      const url: string;
      export default url;
    }
    declare module '*.css' {
      const classNames: Record<string, string>;
      export default classNames;
    }
    Ответ написан
    1 комментарий
  • Как правильно прописать тип number|null?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Интерфейс в TypeScript может иметь несколько объявлений в коде, и TypeScript объединит их все в один общий тип. Судя по ошибке с Вашим ApplicationInit именно это произошло, помимо поля initialPayment объявлены другие поля в другом объявлении, поэтому он их и требует.
    Ответ написан
    Комментировать
  • Как развернуть юнион в интерфейс?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    type Union = 'a' | 'b' | 'c';
    type Obj = {
      [K in Union]: any;
    };
    Интерфейсом насколько помню так нельзя
    Ответ написан
  • Нужен ли TypeScript для написания бэкенда на Node.js?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    Можно ли обойтись обычным js?
    Можно. TypeScript лишь добавляет к JavaScript статическую типизацию в компайл-тайм. После компиляции будет все тот же JS.

    Какие будут плюсы?
    Главный плюс, ИМХО, - скорость разработки за счет подсказок IDE и автодополнения, больше авторефакторингов. Ну и возможность ограничить использование функций/методов от нежелательного использования тоже плюс. А если еще и проектировать доменную модель на типах, то можно сразу видеть, если что-то не сходится, еще до написания логики.

    Не будет ли много лишней писанины по сравнению с чистым js?
    Большинство типов TypeScript способен вывести. Далеко не Хиндли-Милнер конечно, но тоже хорошо. Я пишу больше в ФП стиле, с редкой примесью структурно-процедурного при описании эффектов, так у меня явные указания типов присутствуют только в сигнатурах функций. В самой логике код неотличим от обычного JS, но с хорошей проверкой типов.

    Будет ли сложно хранить скомпилированный js?
    Как и любые другие артефакты сборки, скомпилированный JS хранить не нужно. Компилируйте непосредственно перед выкладыванием на продакшен, а в git храните лишь TS код + настройки компилятора. А в dev среде вообще можно запускаться через модуль ts-node.
    Ответ написан
    Комментировать
  • Проверка значений массива?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    type Validator<U extends string, A extends ReadonlyArray<string>> =
      (U extends A[number] ? true : false) extends true ? A : never;
      
    type U = 'foo' | 'bar';
    const arr0 = ['foo'] as const;
    const arr1 = ['bar'] as const;
    const arr2 = ['foo', 'bar'] as const;
    
    type A0 = Validator<U, typeof arr0>; // never
    type A1 = Validator<U, typeof arr1>; // never
    type A2 = Validator<U, typeof arr2>; // readonly ['foo', 'bar']
    
    const _check0: A0 = arr0; // error
    const _check1: A1 = arr1; // error
    const _check2: A2 = arr2;

    https://www.typescriptlang.org/play?#code/C4TwDgpg...
    Ответ написан
    Комментировать
  • Типизация функции, возвращающей разные объекты?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    const elementSettings = {
      welcome: {
        placeholder: 'Type welcome message...',
        question: '',
        isDescription: false,
        description: '',
        startButtonText: 'Start',
      },
      checkbox: {
        placeholder: 'Type question or statement...',
        question: '',
        isDescription: false,
        description: '',
        options: [] as string[],
        multiple: false,
      }, 
      dropdown: {
        placeholder: 'Type question here...',
        question: '',
        isDescription: false,
        description: '',
        options: [] as string[],
      },
      rating: {
        placeholder: 'Type question...',
        question: '',
        isDescription: false,
        description: '',
        steps: 10,
        defaultRate: 0,
        shape: 'stars',
      },
      text: {
        placeholder: 'Type question...',
        question: '',
        isDescription: false,
        description: '',
        maxLength: 99999,
      }, 
      slider: {
        placeholder: 'Type question...',
        question: '',
        isDescription: false,
        description: '',
        steps: 10,
      }, 
      thanks: {
        placeholder: 'Type message...',
        question: '',
        isDescription: false,
        description: '',
      }
    } as const;
    
    type Editable<T> = { -readonly [P in keyof T]: T[P]; };
    type ElementSettings = typeof elementSettings;
    
    export function getElementSettings<T extends keyof ElementSettings>(type: T): Editable<ElementSettings[T]> {
      if(type in elementSettings) {
        const settings = {...elementSettings[type]};
        if('options' in settings) {
          (settings as {options: string[]}).options = (settings as {options: string[]}).options.slice();
        }
        return settings;
      }
      throw new Error("element type doesn't match!");
    }
    Ответ написан
    Комментировать
  • Как задать дженерик для массива в объекте?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    mport React from 'react';
    import { render } from 'react-dom';
    
    type ITableColumn<D extends Record<string, any>> = {
      property: string;
      format?: (value: any, item: D) => React.ReactNode | string | number;
      header?: any;
      footer?: any;
    };
    
    type ITable<T extends Record<string, any>, D extends Record<string, any>> = {
      isLoading?: boolean;
      columns: ITableColumn<D>[];
      rows: Array<T>;
    };
    
    type IContext<D extends Record<string, any>> = {
      rows: any;
      columns: ITableColumn<D>[];
    };
    
    const getColumns = <D extends Record<string, any>>(): ITableColumn<D>[] => [
      {
        property: 'name',
        format: (value, item) => {
          const { lastName } = item;
          return `${value} ${lastName}`;
        },
        header: 'heade',
        footer: 'footer'
      },
      {
        property: 'count',
        header: 'heade',
        footer: 'footer'
      }
    ]
    
    interface AppProps { }
    interface AppState {
      name: string;
    }
    
    const Table:React.FC<ITable> = ({ isLoading, columns, rows }) => {
      return <h1>table</h1>
    }
    
    const App = () => (
      <div>
        <Table 
          columns={getColumns()} 
          rows={[
            { name: 'userName 0', lastName: 'userLastName 0', count: 10 },
            { name: 'userName 1', lastName: 'userLastName 1', count: 20 },
            { name: 'userName 2', lastName: 'userLastName 2', count: 30 },
          ]} 
        />
        <p>Start editing to see some magic happen :)</p>
      </div>
    );
    
    render(<App />, document.getElementById('root'));
    Ответ написан
    2 комментария
  • Как получить все пропсы компонента React по его экземпляру?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    import type {ComponentType} from 'react';
    import {Button} from 'react-native';
    
    type InferProps<Component> = Component extends ComponentType<infer Props> ? Props : never;
    
    const MyButton = (props: InferProps<typeof Button>) => {
      return <Button title={props.title} onPress={props.onPress} style={{color:'#fff'}} />
    }
    Ответ написан
    Комментировать
  • Как из элементов массива сделать type в typescript?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    const arr = ['a', 'b', 'c'] as const;
    type MyType = (typeof arr)[number];
    Ответ написан
    1 комментарий
  • Почему я получаю ошибку TS2322?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    routes.map((route: IPrivateRoute | IRoute) => route.access // Здесь ошибка, так как в IRoute нет поля access, а значит нет и в юнионе IPrivateRoute | IRoute
      ? <PrivateRoute key={route.path} {...route}/> // а здесь, так как route.access не является тайп-гвардом, и не смотря на условие, тип по прежнему IPrivateRoute | IRoute
    Ответ написан
    Комментировать