jsx-sfc
                                
                                
                                
                                    jsx-sfc copied to clipboard
                            
                            
                            
                        Summary of magical TS type problems
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
        ...
      }
    };
  }
})