currentTarget
а не target, потому что target
может оказаться например span
внутри этой ссылки, а не она сама. cartEmptyImg
. declare module '*.png' {
const png: string;
export default png;
}
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
. Уверены же?..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]
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
- новая фича ts 4.9, в предыдущих версиях того же можно добиться сделав обёртку вида:const narrowRoutesTypeWrapper = <T extends ReadonlyArray<ReadonlyRouteRecordRaw>>(routes: T) => routes;
const routes = narrowRoutesTypeWrapper([ ... ] as const);
const pages: Record<RouteNames, string> = {
first: 'Первая',
second: 'Вторая',
...
}
webpack
- это webpack
, typescript
- это typescript
, всекаешь?sourceMap
. webpack
и typescript
, при работе в лоб webpack
, как сборщик, потребляет sourceMap
из typescript
и генерирует из них свои.webpack
sourceMap
при отключении оных в typescript
могут иметь разный вид в зависимости от настроек, плагинов и режимов: от нормальных, т.к. об этом позаботились плагины или вообще компиляция шла через babel
, до полностью сломанных и бесполезных. MouseEventHandler
переводится как KeyboardEventHandler
как что? Правильно, "обработчик событий клавиатуры". MouseEvent
/KeyboardEvent
)? Да не, бред какой-то.getCity
таки имеет такой тип. И куда же мы передаём этот getCity
? А передаём мы его в onSubmit
. Скажи же мне, друже, submit
- это событие клавиатуры? Или может быть это событие мыши? Ты уверен? Вот и мне кажется что нет. о какой рендер функции идет речь?
<template>
.(под капотом <template>
компилируется в 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>
BehaviorSubject
не стоит, не одному тебе потом с ним работать.class DistinctBehaviorSubject<T> extends BehaviorSubject<T> {
next(value: T) {
if (this.value !== value)
super.next(value);
}
}
Но я на вскидку не скажу какие тут могут быть подводные камни.) 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;
}
})
}
}
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 до полного совпадения.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);
}
}
Date
не может быть children'ом для компонента, и это так. Однако дело в том, что тип у тебя задан неверно, на самом деле у тебя там никакой не Date
а банальный string
. Если ты поправишь типы, то всё заработает и выведет тебе то что ты передаёшь, т.е. 2012-03-23T08:25:44.962Z
.2012-03-23T08:25:44.962Z
в строку Created 7 years ago
надо либо руками написать соответствующую функцию, либо воспользоваться какой-либо библиотекой для работы с датами. Например с помощью moment
это будет выглядеть примерно так:const createdFromNow = `Created ${
moment.duration(moment().diff(data.createdAt)).humanize()
} ago`;