Summary of magical TS type problems
joe-sky opened this issue · 0 comments
joe-sky commented
I record some magical TS type problems here, and then study and analyze the causes. Some of them should be limitation of TS, you can refer to this issue.
The following are type errors and their current solutions:
1. Variables at root child of React.Fragment
✖️ Type error
const App = sfc({
Component() {
return { title: 'hello' };
},
render: ({ data }) => (
<>{data.title}</> // data.title is any
)
})
✔️ Type passed
// You can do either of the following:
const App = sfc({
Component() {
return { title: 'hello' };
},
render: ({ data }) => (
<React.Fragment>{data.title}</React.Fragment>
)
})
const App2 = sfc({
Component() {
return { title: 'hello' };
},
render: ({ data }) => (
<div>
<>{data.title}</>
</div>
)
})
2. If styles is a function, you must use arrow functions to infer types correctly
✖️ Type error
const App = sfc({
Component({ styles: { Container } }) { // Container doesn't exist
return (
<Container>test</Container>
);
},
styles() {
return {
Container: styled.section`
color: #fff;
`
};
}
});
✔️ Type passed
const App = sfc({
Component({ styles: { Container } }) {
return (
<Container>test</Container>
);
},
styles: () => ({
Container: styled.section`
color: #fff;
`
})
});
3. In JSX(TSX is ok), static function must use the return statement to infer types correctly
✖️ Type error
const App = sfc({
Component({ utils, constant: { LAST_NAME } }) { // utils and LAST_NAME are both any
...
},
static: () => ({
constant: {
LAST_NAME: 'sky'
},
utils: {
connectName: (firstName, lastName) => `${firstName}_${lastName}`
}
}),
...
});
✔️ Type passed
const App = sfc({
Component({ utils, constant: { LAST_NAME } }) {
...
},
static: () => {
return { // need to explicitly use the return statement
constant: {
LAST_NAME: 'sky'
},
utils: {
connectName: (firstName, lastName) => `${firstName}_${lastName}`
}
};
},
...
})
4. In JSX(TSX is ok), when Component is arrow function, if there is any type in the function parameter during type inference, the whole data will be inferred as any
✖️ Type error
const App = sfc({
Component: () => {
return {
onChange: e => console.log(e.target.value)
};
},
render: ({ data }) => (
<button onClick={data.onChange}>test</button> // onChange is any
)
})
✔️ Type passed
const App = sfc({
Component() { // need to use the object property function syntax
return {
onChange: e => console.log(e.target.value)
};
},
render: ({ data }) => (
<button onClick={data.onChange}>test</button>
)
})
5. The data parameter of the render function doesn‘t support direct deconstruction
✖️ Type error
const App = sfc({
Component() {
return { title: 'hello' };
},
render: ({ data: { title } }) => (
<div>{title}</div> // title is any
)
})
✔️ Type passed
const App = sfc({
Component() {
return { title: 'hello' };
},
render: ({ data }) => {
const { title } = data;
return <div>{title}</div>;
}
})
This problem should be caused by the type circular reference problem of TS, there is no perfect solution for this problem. At present, my solution is to use extends
to remove a layer of circular reference. For details, please refer to here.
6. Define functions in static function
✖️ Type error
const App = sfc({
Component({ func, func2 }) {
...
},
static: () => {
return {
func: (a: string, b = false) => { // with default parameters
...
},
func2() {
...
}
};
}
})
✔️ Type passed
const App = sfc({
Component({ func, func2 }) {
...
},
static: () => {
function func(a: string, b = false) {
...
}
return {
func,
func2: (a: string, b?: boolean) => { // must use arrow function
...
}
};
}
})