Comments 13
Очень ценная статья, спасибо что поделились! Но у меня остались кое какие вопросы. Для чего в компоненте Figure создавать 3 новых интерфейса для фигур когда можно было бы props под названием type прописать в интерфейсах самих фигур? Таким образом код работал бы точно так же и был бы короче.
Спасибо за интересную статью.
Сам я пишу на JavaScript + Vue и никогда не писал ни на TS ни на React. Вы заставили меня с ними поковыряться, потому что привели реальный интересный пример.
Честно говоря я бы предпочел сделать через merge типов (&), для меня это выглядит более лаконичным. Уверен что все подсказки и бенефиты из ваших примеров работают и здесь.
type CircleProps = { radius: number }
const Circle = ({ radius }: CircleProps) => ...
type RectProps = { width: number; height: number }
const Rect = ({ width, height }: RectProps) => ...
type FigureCircle = { type: 'circle' } & CircleProps
type FigureRect = { type: 'rect' } & RectProps
const Figure = (props: FigureRect | FigureCircle) => {
switch (props.type) {
case 'circle':
return <Circle {...props} />
case 'rect':
return <Rect {...props} />
default:
return null
}
}
Но вообще мне кажется вы недооцениваете JavaScript. Все тоже самое, со всеми подсказки и плюшками возможно сделать и на JS.
function getFigure() {
return {
circle({ radius = 0 }) {
return <div>I'm a circle {radius}</div>
},
rect({ width = 0, height = 0 }) {
return <div>I'm a rect {width}, {height}</div>
}
}
}
export default function App() {
const type = 'rect'
return (
<>
{getFigure()[type]({ width: 123, height: 321 })}
{getFigure().circle({ radius: 42 })}
</>
)
}
И сюрприз-сюрприз, получать хинты можно прямо в IDE, включая тип number для аргументов. Ниже результат.
А при правильно настроенном ESLint и юнит тестах, разницы с TS вы не заметите, кроме более простого и понятного кода.
Это очень хорошо, что вы решили попробовать это на практике!
Насчет использования &, то здесь скорее нет технической разницы и каждый может выбрать то, что ему по душе. Я, например, предпочитаю использовать type для сущностей (User, Card, Address), функций ((name: string)=>void) или для объединения нескольких interface. А interface использую для всего остального.
Что касается реализации на js, то в статье описаны проблемы, которые возникают на нем. В вашем примере вам даже пришлось отказаться от jsx.
Я лишь хотел рассказать, как можно использовать typescript правильно, если он есть в проекте. Чтобы от него была действительная польза, а не только упоминание в стэке проекта.
Согласен, тут кому что нравится!
Кстати, сначала я не понял зачем вы используете литеральный тип "type", но проигравшись понял что иначе typescript не понимает что речь идёт про окружность например. Хотя казалось бы если указан radius, то ни width ни height уже быть не может.
Простейший пример:
interface Circle { radius: number}
interface Rect { width: number, height: number}
function test (fig: Circle | Rect) {
if ("radius" in fig) {
console.log(fig.radius) // code completion gives only radius
} else {
console.log(fig.width) // gives only width and height, nice!
console.log(fig.radius) // compilation error, super!
}
}
test({radius: 1, width: 2}) // no errors, wtf?
Это странно!
Хотя казалось бы если указан radius, то ни width ни height уже быть не может.
Это особенность структурной типизации в TS. Лишние поля не считаются ошибкой, за исключением ситуации когда объект передаётся как литерал ({ ... }
). В этом случае TS предполагает что "явно что-то идёт не так", и пытается бить по рукам за неправомерные поля. Но видимо не сильно вникает в то какие же поля правомерны:
test({radius: 1, width: 2}) // no error
test({radius: 1, wrong: 3, width: 2}) // "wrong" - error
const a = {radius: 1, wrong: 3, width: 2}
test(a) // no error
На ночь пришла идея как избавиться от импорта интерфейсов подкомпонентов и обьявления новых интерфейсов(типа IFigureRectangle). При таком виде, если мы хотим добавить новый подкомпонент, то просто добавляем еще одну строчку в IFigure.
Типы ориентированы под функциональный компонент, но можно подправить, если пригодится для классовых.
Круто! Не могли бы дать ссылку codesandbox с обновленной версией?
Если я правильно понял идею, то посмотрите в сторону React.ComponentProps<typeof Comp>
, вместо Parameters<C>[0]
.
Использование Typescript для создания react компонента «Простой фабрики»