...form.formParams.errorList[formIndex]
// допустим у нас есть следующие переменные с типами:
let obj: Obj;
let value: T;
// мы их собираем в такой объект:
const newObj = { ...obj, k: value };
тип для newObj компилятор будет выводить из контекста и получит Obj & { k: T }
, ровно из того, что мы объединяем эти сущности.let objs: Obj[];
let value: T;
let key: string;
const newObj = { ...objs[1], [key]: value };
Тип на выходе будет ужеObj & { [key: string]: string }
, хотя objs на это раз у нас массив, компилятор видит, что мы берем индекс.Obj[]
у нас будет Obj[] | Obj[][]
?Obj | Obj[]
.(Obj | Obj[]) & { [key: string]: string }
, тут еще Важно понимать, что типы в TS - это чистая математика, и данный тип полностью равносилен такому:(Obj & { [key: string]: string }) | (Obj[] & { [key: string]: string })
{ [key: string]: string }
? Мы сможем свернуть тип, упростив уравнение на типах:(Obj & Obj) | (Obj[] & Obj)
(Obj) | (Obj[] & Obj)
(Obj & Obj[]) | (Obj & Obj)
(Obj & Obj[]) | (Obj)
Obj & Obj[]
И это именно тот тип, который получил у Вас компилятор export type ApiResponse<T> =
| { errors: Array<{ [key: string]: string }> }
| { data: Array<T> }
| { text: string };
abstract class Api<T> {
protected readonly baseUrl: string;
protected readonly endPoint: string;
protected get url(): string {
return new URL(this.endPoint, this.baseUrl).toString();
}
protected constructor(baseUrl = '/', endPoint: string) {
this.baseUrl = baseUrl;
this.endPoint = endPoint;
}
protected async http({
url = '',
method = 'GET',
data = undefined,
}: RequestData): Promise<ApiResponse<T>> {
try {
const response: Response = await fetch(
(new URL(url, this.url).toString()),
{ method, body: data }
);
return await response.json();
} catch (e) {
console.error(e.message);
return { text: e.message };
} finally {
// finnally
}
}
}
class ApiPost extends Api<Post> {
constructor(baseUrl: string, endPoint: string) {
super(baseUrl, endPoint);
}
async list(count?: number): Promise<false | Array<Post>> {
const response: ApiResponse = await this.http({
url: `${count ? `?_limit=${count}` : ''}`,
});
if (Array.isArray(response))
return response;
return false;
}
}
type ChooseKeys<O, T> = {
[K in keyof O]: O[K] extends T ? K : never;
}[keyof O];
// 'number' | 'alsoNumber'
type Test1 = ChooseKeys<{
number: number;
alsoNumber: number;
string: string;
anotherString: string;
bool: boolean;
}, number>;
// 'number' | 'alsoNumber' | 'bool'
type Test2 = ChooseKeys<{
number: number;
alsoNumber: number;
string: string;
anotherString: string;
bool: boolean;
}, number | boolean>;
type AsCamelCase<S extends string> = S extends `${infer A}_${infer B}` ? `${A}${Capitalize<AsCamelCase<B>>}` : S;
type CamelCaseKeysRecord<T> = T extends Record<PropertyKey, unknown> ? {
[K in string & keyof T as AsCamelCase<K>]: CamelCaseKeysRecord<T[K]>;
} & {
[K in Exclude<keyof T, string>]: CamelCaseKeysRecord<T[K]>;
} : T;
function toCamelCaseKeys<T>(val: T): CamelCaseKeysRecord<T> {
if (Array.isArray(val)) {
return val.map(toCamelCaseKeys) as unknown as CamelCaseKeysRecord<T>;
}
if (typeof val === 'object') {
return Object.fromEntries(Object
.entries(val)
.map(([k, v]) => [
k.replace(/_+(.)/g, (_, g) => g.toUpperCase()),
toCamelCaseKeys(v as Record<string, unknown>),
])) as CamelCaseKeysRecord<T>;
}
return val as unknown as CamelCaseKeysRecord<T>;
}
много где начинает ругаться например что у ControlsProps нет метода foreachвсе правильно ругается, по Вашим типам ControlsProps - это plain object, где ключом может быть любая строка, а все значения - ControlProps или массив ControlProps. У произвольного объекта нет метода forEach.
Не подскажете как вообще правильные ситуации обрабатывать, может подход с ControlsProps | ControlsProps[] в корне неправильныйhttps://www.typescriptlang.org/docs/handbook/2/nar...
import 'vue'; // нужно чтоб слинковаться с базовыми модулем и интерфейсом
declare module 'vue' { // расширяем модуль
import vClickOutside from 'v-click-outside'; // подключаем типы плагина
interface Vue { // расширяем интерфейс
// прописываем нужные расширения
vClickOutside(): typeof vClickOutside;
}
}
лишь ошибки типизации которые не мешают компиляцииЕсли проект компилируется - то все типы сошлись. Если проект компилируется, а ошибка все таки есть - значит типы криво описаны.
Как проверять все файлы проекта на правильность типизации?Скомпилировать проект.
Но, если допустим, изменить какой-нибудь тип (интерфейс), а файл, который на него ссылается и использует будет закрыт, то ошибку никакую не выдаст.И это хорошо. Если TypeScript LSP будет перепроверять весь проект на каждый чих, а не только открытые файлы, то писать что-то сложнее todo листа будет невозможно...
Знаю, существуют ESLint, TSLint.Они тоже для статического анализа, но для немного другого. В VSCode они так же чекают только открытые файлы, из тех же соображений производительности.
TSLint в VSCode прям жутко тугойTSLint официально deprecated, вместо него стоит использовать плагин к eslint, заодно можете мой конфиг попробовать.
Типы? Есть JSDOCJSDoc в плане типов не умеет и 10% того, что умеет TypeScript. Кроме того, никто в здравом уме не пишет JSDoc на приватную логику, а значит проверки типов там не будет. JSDoc не гарантирует корректность рефакторинга, а вот благодаря TypeScript я, опять таки, точно не забуду обновить JSDoc.
многие библиотеки nodejs не имеют типовможет лет 5 назад так и было, но сейчас встретить библиотеку без типов - скорее исключение. Если библиотека популярная, но не предоставляет типов, скорее всего их уже написал кто-то другой, достаточно просто установить одноименный модуль из npm скоупа types и все будет работать само.
Поддержка браузерами скомпилированного кода? Да какбы почти весь JS имеет поддержку 95%+, тот же Babel уже забыл когда использовал.Вообще это не основная задача компилятора TypeScript, а опциональная возможность. И babel + preset-env с ней справляются гораздо лучше. И никто не мешает использовать их вместе. А еще думаю вопрос времени, когда кто-то напишет оптимизатор кода использующий информацию о типах из TS.
Примерно в каждой второй есть инстансы, на которые смотришь - и чешешь репу - а как называется тип этой переменной в @types/?
import {someObject} from 'some-library';
type TypeFromValue = typeof someObject;
const valueCopy: TypeFromValue = {
...someObject,
type: 'overrides',
with: 'type check',
};
и кстати, вот пример того что JSDoc типы не умеют.//@ts-checkи почти везде останется бесполезный any.
import { FC, memo } from "react";
interface IAppProps {
title: string;
}
interface IStatic<T> {
someStaticString: string;
someStaticObj: T;
}
const someObj = {
SOME_KEY: "SOME_VALUE"
};
const App: FC<IAppProps> & IStatic<typeof someObj> = memo((props) => {
const { title } = props;
return <>{title}</>;
});
App.someStaticString = "someStaticString";
App.someStaticObj = someObj;
export default App;
enum UserActionTypes { FETCH_USERS = "FETCH_USERS", FETCH_USERS_SUCCESS = "FETCH_USERS", FETCH_USERS_ERROR = "FETCH_USERS", }
enum UserActionTypes {
FETCH_USERS = 'FETCH_USERS',
FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS',
FETCH_USERS_ERROR = 'FETCH_USERS_ERROR',
}
interface Field {
value: any,
validation?: FieldValidation
}
interface InputField extends Field {
value: string,
placeholder: string,
textarea?: boolean,
maxChars?: number,
type?: 'text' | 'email' | 'password' | 'tel' | 'url',
dark?: boolean,
disabled?: boolean,
onInput?: () => any,
onKeyDown?: () => any,
onFocus?: () => any,
onBlur?: () => any
}
аналогично этому:interface Field {
value: any,
validation?: FieldValidation
}
type InputField = Field & {
value: string,
placeholder: string,
textarea?: boolean,
maxChars?: number,
type?: 'text' | 'email' | 'password' | 'tel' | 'url',
dark?: boolean,
disabled?: boolean,
onInput?: () => any,
onKeyDown?: () => any,
onFocus?: () => any,
onBlur?: () => any
}
interface BaseField {
value: any,
validation?: FieldValidation
}
interface InputField extends BaseField {
value: string,
placeholder: string,
textarea?: boolean,
maxChars?: number,
type?: 'text' | 'email' | 'password' | 'tel' | 'url',
dark?: boolean,
disabled?: boolean,
onInput?: () => any,
onKeyDown?: () => any,
onFocus?: () => any,
onBlur?: () => any
}
interface SelectField extends BaseField {
options: string[],
value: string,
placeholder: string,
dark?: boolean,
disabled?: boolean,
onSelect?: () => any,
onKeyDown?: () => any,
onFocus?: () => any,
onBlur?: () => any
}
type Field = InputField | SelectField;
const fields = ref<Record<string, Field>>({
name: {
value: '1',
placeholder: '123'
},
});
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@*": ["./src/modules/*"]
}
}
}
остальные параметры по своему вкусуconst isFormPropsType = (v: FormPropsType | {path: string}): v is FormPropsType => typeof (v as FormPropsType).formName === 'string';
const authForms:AuthFormsType = {
login:(props) =>{
if(isFormPropsType(props)){
return <Login
formName={props.fromName}
onSubmit={props.onSubmit}
changeFormStateHandler={props.changeFormStateHandler}
/>
}
return <Login path={props.path} />
},
const authForms:AuthFormsType = {
login:(props) => {
return <Login {...props} />
},
submitHandlersCreator<AuthFormsDataTypes[typeof commonName]>({
dispatch,
path: `${location.pathname}${location.search}`,
actionName:commonName
})
Но у нее должен быть литеральный тип или юнион литеральных типов, притом из множества ключей AuthFormsDataTypes