Ответы пользователя по тегу TypeScript
  • Почему вылетает error 'Cannot read property 'subscribe' of undefined'?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    getCerts(): Observable<any> {
      // ...
      return new Observable(observer => {
        chrome.runtime.sendMessage(editorExtensionId, message, res=> {
          // ...
           observer.next(result);
           observer.complete();
        })
    }
    })


    Домашняя работа - сократить код используя https://rxjs.dev/api/index/function/bindCallback
    Ответ написан
    Комментировать
  • Какой тип должен быть у переменной, которая передается как ключ для доступа к значению, находящимуся в HTMLElement?

    Xuxicheta
    @Xuxicheta
    инженер
    offset и singleSlider должны быть нормально типизированы.
    offset хотя бы как keyof HTMLElement, но вообще лучше конкретно перечень возможных значений.
    Ответ написан
    2 комментария
  • Как получить доступ к chrome в Angular?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    не надо слушать вредные советы :)
    Установите типы для хрома https://www.npmjs.com/package/@types/chrome

    а потом где-нибудь создаем токен для инжектора.
    export const CHROME = new InjectionToken('CHROME');

    в провайдерах апп-модуля по этому токену провайдим значение
    providers: [
      ...
      {
        provide: CHROME,
        useValue: window.chrome,
      }
      ...
    ]

    В конструкторе вашего компонента или сервиса
    constructor(
      @Inject(CHROME) private chrome: chrome,
    )


    Это делается для того чтобы иметь возможность когда нужно эту зависимость подменить. Например подсовывать моки для тестирования.
    Но вообще можно и просто chrome или window.chrome вызвать, если вы не паритесь по поводу подмен и вообще слежением за зависимостями. Главное объяснить тайпскрипту что это валидное имя. Ну т.е. доустановить типы.

    Я могу ошибиться в мелочах, т.к. не проверял, но правильный способ в общих чертах описал.
    Ответ написан
    2 комментария
  • Как использовать this в стрелочной функции в typescript?

    Xuxicheta
    @Xuxicheta
    инженер
    Судя по данному PR в typescript можно использовать this в стрелочных функциях

    эм.. я может не туда смотрю, но там написано как раз что это бессмысленно. И ни одного примера типизации this в стрелочных нет.
    This formulation is OK because lambda (=>) doesn't bind this, so this comes from the class instead of from the implementing function.


    А вообще можно объявив сначала тип для функции, вот так
    https://www.typescriptlang.org/play/?noImplicitThi...
    Но оно тоже работает криво.
    А зачем вам вообще это понадобилось?
    Ответ написан
    6 комментариев
  • Почему не происходит запись в базу indexedDB?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    На вид все вроде ок, надо дебажить, перерести куда-нибудь в песочницу.
    Пока могу предложить свою обертку к indexedDB, вроде работает, не тестировал толком. Может наведет на размышление.
    DbService

    export interface DbConfig {
      /** prefix for indexed db name */
      name: string;
      /** keyPath in indexed db */
      keyPath?: string;
      /** keyPath of indexed db */
      objectName: string;
    }
    
    @Injectable()
    export class DbService<T = any> {
      private version = 1;
      private db$: Observable<IDBDatabase> = this.createDb(this.config.name, this.version);
    
      constructor(
        @Inject(DB_CONFIG) private config: DbConfig,
      ) { }
    
      private createDb(name: string, version?: number): Observable<IDBDatabase> {
        const openRequest: IDBOpenDBRequest = indexedDB.open(name, version);
    
        openRequest.onupgradeneeded = (evt: IDBVersionChangeEvent) => this.onUpgradeNeeded(openRequest);
    
        return this.fromIDBRequest(openRequest).pipe(
          shareReplay(1),
        );
      }
    
      private onUpgradeNeeded(openRequest: IDBOpenDBRequest): void {
        const db = openRequest.result;
        if (db.objectStoreNames.contains(this.config.objectName)) {
          return;
        }
        db.createObjectStore(this.config.objectName, { keyPath: this.config.keyPath });
      }
    
      public save(value: T, key?: IDBValidKey): Observable<IDBValidKey> {
        return this.db$.pipe(
          mergeMap((db: IDBDatabase) => this.fromIDBRequest(
            this.createIDBObjectStore(db, 'readwrite').put(value, key)
          )),
        );
      }
    
      public saveAll(values: T[]): Observable<IDBValidKey[]> {
        return from(values).pipe(
          mergeMap((value: T, index: number) => this.save(value, index)),
          toArray(),
        );
      }
    
      public delete(key: string): Observable<undefined> {
        return this.db$.pipe(
          mergeMap(db => this.fromIDBRequest(
            this.createIDBObjectStore(db, 'readwrite').delete(key),
          ))
        );
      }
    
      public clear() {
        return this.db$.pipe(
          mergeMap(db => this.fromIDBRequest(
            this.createIDBObjectStore(db, 'readwrite').clear(),
          ))
        );
      }
    
      public retreive(key: string): Observable<T> {
        return this.db$.pipe(
          mergeMap(db => this.fromIDBRequest(
            this.createIDBObjectStore(db, 'readonly').get(key)
          )),
        );
      }
    
      public retreiveAll(): Observable<T[]> {
        return this.db$.pipe(
          mergeMap(db => this.fromIDBRequest(
            this.createIDBObjectStore(db, 'readonly').getAll()
          )),
        );
      }
    
      private createIDBObjectStore(db: IDBDatabase, mode: IDBTransactionMode): IDBObjectStore {
        const transaction: IDBTransaction = db.transaction(this.config.objectName, mode);
        return transaction.objectStore(this.config.objectName);
      }
    
      private fromIDBRequest<R>(idbRequest: IDBRequest<R>): Observable<R> {
        return new Observable<R>(observer => {
          idbRequest.onsuccess = (evt: Event) => {
            observer.next(idbRequest.result);
            observer.complete();
            evt.stopPropagation();
          };
          idbRequest.onerror = (evt: Event) => {
            observer.error(idbRequest.error);
            evt.stopPropagation();
          };
        });
      }
    
      public selectDb() {
        return this.db$;
      }
    }

    Ответ написан
  • Как настроить/использовать модули для typescript?

    Xuxicheta
    @Xuxicheta
    инженер
    Простейший конфиг webpack.config.js

    const path = require('path');
    
    module.exports = {
      entry: './src/index.ts',
      devtool: 'inline-source-map',
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            use: 'ts-loader',
            exclude: /node_modules/,
          },
          {
            test: /\.css$/i,
            use: ['style-loader', 'css-loader'],
          },
        ],
      },
      resolve: {
        extensions: ['.tsx', '.ts', '.js'],
      },
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist'),
      },
    };


    исходники должны лежать в папке src, стартовый файл index.ts
    сборка webpack --mode=production
    разработка с hmr
    webpack-dev-server --mode development --inline --hot


    зависимости (версии уже надо обновить наверно)
    "devDependencies": {
        "css-loader": "^3.0.0",
        "ts-loader": "^6.0.4",
        "tslint": "^5.18.0",
        "tslint-config-airbnb": "^5.11.1",
        "typescript": "^3.5.2",
        "webpack": "^4.35.0",
        "webpack-cli": "^3.3.5",
        "webpack-dev-server": "^3.7.2"
      }


    tsconfig.json (думаю не очень хороший, но рабочий)
    {
      "compilerOptions": {
        "allowJs": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "incremental": true,
        "module": "es6",
        "noImplicitAny": false,
        "outDir": "./dist/",
        "sourceMap": true,
        "target": "es2016"
      }
    }


    tslint.json - правила для линтера
    {
      "extends": "tslint-config-airbnb",
      "rules": {}
    }


    в html
    <script src="dist/bundle.js"></script>
    Ответ написан
    Комментировать
  • Angular2: как при авторизации обновить header.component?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    @Injectable()
    export class UserService {
        isAuthenticated$ = new ReplaySubject(1);
      
        constructor(private http: HttpClient) { }
         
        getUser() {
            return this.http.get('/server/api/userService').pipe(
              tap(data => this.isAuthenticated$.next(!!data['login'])),
            );
        }
    }


    export class HeaderComponent implements OnInit {
          user$: Observable<User>;
          isAuthenticated$: Observable<boolean>;
    
         constructor(private userService: UserService) { }        
    
          ngOnInit() {
            this.user$ = this.userService.getUser();
            this.isAuthenticated$ = this.userService.isAuthenticated$;
         }
    }


    При использовании AsyncPipe достаточно этого, хотя даже isAuthenticated - лишний.
    Ответ написан
    9 комментариев
  • Angular: как получить данные от апи и объявить переменные глобально?

    Xuxicheta
    @Xuxicheta
    инженер
    пройстейший сервис чтобы один раз спросить бэкенд, закешировать и отдавать потом компонентам, чтобы они подписывались.
    @Injectable({
      providedIn: 'root'
    })
    class UserService {
      private user$;
      
      constructor(private http: HttpClient) {}
    
      fetchUser(): Observable<User> {
        if (!this.user$) {
          this.user$ = this.http.get('/server/api/userService').pipe(shareReplay(1))
       }
       return this.user$;
     }
    }


    Его никуда не надо загружать, просто помещать в конструктор компонентов, там где он будет использваться. Подходит для данных, актуальных и неизменных на протяжении всей жизни приложения.

    И этот код не принесет вам пользы, если вы не понимаете что он делает.
    Ответ написан
  • Как реализовать правильно данный Guard?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
      return this.auth.getUser().pipe( map(user => user.err == 0));
    }
    Ответ написан
  • Что не так? В чем ошибка?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    потому что
    export class Task1Module {
      constructor(
        public distance:Observable<distance>,
        public convert_to:string
      ) {
      }
    }


    поле distance это Observable, а у него нет свойства unit
    а потом вы еще convert_out.value пытаетесь число присвоить, а оно является строкой.

    Пишите все по человечески и такое будет сразу видно.
    Ответ написан
  • Как блокировать @HostListener прослушку?

    Xuxicheta
    @Xuxicheta
    инженер
    Не совсем понял момент насчет setTimeout.
    В коде мат-диалога есть такое
    // The logic that disposes of the overlay depends on the exit animation completing, however
          // it isn't guaranteed if the parent view is destroyed while it's running. Add a fallback
          // timeout which will clean everything up if the animation hasn't fired within the specified
          // amount of time plus 100ms. We don't need to run this outside the NgZone, because for the
          // vast majority of cases the timeout will have been cleared before it has the chance to fire.
    this._closeFallbackTimeout = setTimeout(() => {
            this._overlayRef.dispose();
          }, event.totalTime + 100);

    overlayRef.dispose() производит отвязывание от событий клавиатуры.
    Как я понял вы по той же причине хотите таймаут, т.к. видимо OnDestroy срабатывает раньше отписок, хотя по коду должен бы после.
    Материал довольно сложно устроен и я не стал бы на это полагаться :)

    Не проще ли сделать глобальный серсвис с событями клавиатуры, диалоговые окна запускать с disableClose: true, при открытие окна блокировать события сервиса для всех, кроме диалога, а сам диалог закрывать руками при получении события эскейпа и сразу снимать блокирование?
    Ответ написан
  • Можно ли назначить тип для пустого объекта в typescript?

    Xuxicheta
    @Xuxicheta
    инженер
    посмотрите как можно и нельзя использовать
    тип {} даже более широкий, чем object. Ему соответствует что угодно кроме null и undefined, т.е. любой тип наследуется от него.
    Поэтому присвоить ему можно что угодно.
    А прочитать ничего нельзя.
    В случае юниона надо сначала сузить тип с помощью тайпгарда, чтобы убедить TS что свойство есть.
    Ответ написан
    2 комментария
  • Можно ли обойтись без кастования типов при присваивании строки переменной с литеральным типом?

    Xuxicheta
    @Xuxicheta
    инженер
    попробуй так. мож прокатит
    /**
     * @template {string} T
     * @param {T} encryptOptions
     */
    function somefunction(encryptOptions)


    это равнозначно в ts
    function somefunction<T extends string>(encryptOptions: T)

    правда если ты передаешь объект, нужно будет сделать отдельный тип с дженериком чисто для этой функции.
    Ответ написан
    2 комментария
  • Как связать компоненты между страницами?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    Вот вам пример простейшего стора для обмена данными
    https://ng-run.com/edit/RmyDdpUtMUe0V5cmb6X7
    Ответ написан
    Комментировать
  • Почему не валидируется вложенная форма?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    <mat-card [formGroupName]="body">
    у компонента нет поля body
    ошибки типа
    Cannot find control with unspecified name attribute
    в консоли не смутили чтоли?

    Квадратные скобки убери и все заработает. Вот так
    Ответ написан
    Комментировать
  • Как оптимизировать динамический truncate середины списка?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    Вместо явного дочернего li создаешь невидимый с opacity: 0; position: absolute, чтобы вывести его из потока и визуально скрыть. В отличии от display: none элемент будет отренденер и будут доступны его геометрические размеры.
    Получаешь его длину, получаешь длину родителя, усекаешь массив пока li не станет короче родителя.
    И только потом показываешь основной li с усеченным массивом.
    Можно использовать тот же li, только класс поменять ему.
    Ответ написан
    2 комментария
  • Как в Ангулар SPA работать с браузерными кнопками Вперед\Назад?

    Xuxicheta
    @Xuxicheta Куратор тега Angular
    инженер
    Чтобы заработала браузерная "Назад" браузеру нужно знать что был переход по маршруту.
    Т.е. нажатие на таб должно изменять адрес в командной строке и изменилось window.history
    В спа фреймворках удобной абстракцией для этого служит роутер.
    В ангуляре это будет выглядеть примерно так https://ng-run.com/edit/zmORlCu2qJ5hI5G7Ggbk

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

    upd:
    Можно сделать все в одном компоненте при желании.
    Сделать роут вида
    { path: 'tabs/:tab', component: TabsComponent },
    А в самом табс следить route.params.subscribe(({ tab}) => { ... })
    Ответ написан
    5 комментариев
  • Как определить тип переменной в цикле?

    Xuxicheta
    @Xuxicheta
    инженер
    Так надо не для some, а для data тип определять.
    const myIterable: unknown = [1,2,3,4,5];
    
    for (const entry of <IterableIterator<number>>myIterable) {
        console.log(entry);
    }


    Ну или как выше написали, через тайпгарды.
    Ответ написан
  • Как определить из 2 типов: 1 или 2?

    Xuxicheta
    @Xuxicheta
    инженер
    Есть некоторый костыль, который можно откопать в пропозалах тайпскрипта. Возможно когда-нибудь что-то такое станет частью языка. Но эта фигня предполагает что не может быть пересекающихся свойств.

    https://www.typescriptlang.org/play/index.html#cod...

    А вообще надо просто писать так, чтобы не надо было так извращаться. Сделать тайпгарды на крайняк.
    Ответ написан
    Комментировать