Я пытаюсь построить конструктор форм для React. Для первой версии функции включают в себя абстрагирование состояния формы, возврат его при отправке формы и проверку обязательных полей.
Для последней функции мне нужно определить интерфейс без известных имен свойств, но объекты свойств должны иметь определенную форму. Поэтому я использую типы индексов. Но поскольку мне нужно связать поле со свойством состояния, я бы хотел, чтобы средство проверки типов проверило, существует ли это имя свойства. Кто-нибудь знает способ сделать это?
Сейчас он не проверяет, существует ли имя свойства, он просто использует строку, которая во время выполнения должна соответствовать правому свойству в состоянии.
Я использую последнюю версию TypeScript (на момент написания в любом случае), которая является 3.4.4.
Вот что у меня сейчас:
interface Form {
(defaultState: FormState, fields: any[], options: FormOptions): (newState: FormState) => JSX.Element
}
interface FormState {
[key: string]: FormStateItem
}
interface FormStateItem {
value: any
required: boolean
}
interface FormOptions {
title: string
buttonText: string
formClass?: string
wrapperClass?: string
showTitle?: boolean
}
interface Field<T> extends FieldFunction<T>, FieldOptions<T> {}
interface FieldFunction<T> {
(value: T, callback: FieldCallback<T>): JSX.Element
}
interface FieldOptions<T> {
id: string
setId: (id: string) => Field<T>
}
interface FieldCallback<T> {
(newValue: T): void
}
interface FieldWrapper {
<T>(options: any): Field<T>
}
interface FieldBuilder {
<T>(callback: FieldCallback<T>): Field<T>
}
Я хочу, чтобы функция setId проверяла тип свойства FormState как строковый литерал, если это возможно. Было бы здорово, если бы это можно было проверить во время компиляции, а не во время выполнения.
Если я не задал этот вопрос правильно, или он мог бы быть лучше, дайте мне знать. Я не задавал вопрос здесь раньше. : P
Edit:
Я выяснил, как позволить проверке типов проверять, является ли пользователь набора библиотек правильным свойством состояния в setId, используя сопоставленные типы. Однако TypeScript не может определить типы при реализации интерфейса или типа.
Итак, новый дизайн такой:
interface Form {
<State>(
defaultState: FormState<State>,
fields: Field<any, State>[],
options: FormOptions
): (callback: FormCallback<State>) => JSX.Element
}
interface FormCallback<State> {
(newState: FormState<State>): void
}
type FormState<State> = { [K in keyof State]: FormStateItem<any> }
type FormStateItem<T> = T
interface FormOptions {
title: string
buttonText: string
formClass?: string
wrapperClass?: string
titleClass?: string
showTitle?: boolean
}
interface Field<T, S> extends FieldFunction<T>, FieldOptions<T, S> {}
interface FieldFunction<T> {
(
value: T,
callback: FieldChangeCallback<T>,
validationCallback: FieldChangeCallback<T>,
state: FieldItemState
): JSX.Element
}
interface FieldOptions<T, S> {
id: keyof S
setId: (id: keyof S) => Field<T, S>
required: boolean
validationCallback: FieldValidationCallback<T>
}
interface FieldChangeCallback<T> {
(newValue: T): void
}
interface FieldWrapper {
<T, S>(
options: FieldWrapperOptions,
parse: Parser<T>,
validationCallback: FieldValidationCallback<T>
): Field<T, S>
}
interface FieldWrapperOptions {
name: string
required: boolean
className?: string
placeholder?: string
wrapperClass?: string
labelClassName?: string
successText?: string
invalidText?: string
emptyText?: string
}
interface FieldBuilder {
<T, S>(
fieldFunction: FieldFunction<T>,
validationCallback: FieldValidationCallback<T>,
required: boolean
): Field<T, S>
}
interface FieldValidationCallback<T> {
(state: FormStateItem<T>): boolean
}
Это пример того, как он будет использоваться:
type FormExampleState = {
name: string
age: '' | number
}
const defaultFormState: FormExampleState = {
name: '',
age: ''
}
const exampleForm = form<FormExampleState>(
defaultFormState,
[
text<string, FormExampleState>(
{
name: 'Name',
required: true
},
value => value,
state => state.length > 0
).setId('name'),
text<'' | number, FormExampleState>(
{ name: 'Age', required: true },
value => parseInt(value),
state => state.toString.length > 0
).setId('age')
],
{
title: 'Form Example 1',
buttonText: 'Send'
}
)
/* And inside a React component */
exampleForm(newState => console.log(newState))
Итак, я предполагаю, что возникает вопрос: как я могу спроектировать это так, чтобы включить вывод типа TypeScript?