3 ms·
When you use TypeScript with React (and want sound typings per policy so any type erasures/widenings are considered bad practice) Hooks are a big leap forward.
by littlecranky67 8y ago
When you use TypeScript with React (and want sound typings per policy so any type erasures/widenings are considered bad practice) Hooks are a big leap forward.
Typing classes was much to verbose, and the type system of TypeScript to this day is not a perfect fit to type class-based components. Hooks solve this problem - probably unintentionaly.
Example: With Hooks you almost never need to explicitly type the type of state of a component, as whatever you pass to useState() will auto-infer the type. While when using class-based Components, you'd have to specify the state type as a generic argument; you'd have to manually specify it when you set a state using this.setState() as partial types were allowed due to reacts auto-merging of the passed argument to the state. And you'd have to use some kind of Partial<State> type when assigning only some values of the state in the property constructor.
Similar issues exist when using the static defaultProps field in TypeScript - the type couldn't be inferred from the generic class argument, as a Partial<Props> type is valid for the defaultProps field. As a result, the defaultProps (as well as the default state property) is cast as the empty object type {} by default if you don't explicitly assign a type - which makes you lose basically all the type information because every object is assignable to {} in TS.
And if you ever tried to strictly type your HOCs or renderProps you know how hard it was, sometimes utilizing the conditional/mapped generic types feature of TS. This is completely gone with Hook.
I could go on telling how stupid it is when you disable nullable types in TS (strict mode) because TS will not let you use any non-nullable prop inside of render() - even if that prop is initialized in the static defaultProps field. You'd have to override the non-null check using TS exclamation point operator. This is also gone with Hooks.
I don't know if they designed the Hooks API with TS in mind or if this is completely accidental, but if you migrate a TS project with strict mode enabled to Hooks, you will see how much implicit type annotations can be dropped, and still retain strict type checks. Big fan of it.
- williamdclt 8y agoif you migrate a TS project with strict mode enabled to Hooks, you will see how much implicit type annotations can be dropped I think you meant "explicit" I agree with you. I've not been too bothered by typing state and default props, but higher-order-components are almost impossible to type properly (a good typedef doubles the LOC count). I don't like that hooks don't abstract away the shared behaviour as well as HOCs (the component is unaware of the HOC while it it aware of the hook), but being explicit isn't a bad thing either and it's definitely worth it if it makes typing a non-problem
- thatswrong0 8y agoI wouldn’t say components are unaware of HoCs.. they are implicitly based on the props they accept.
- williamdclt 8y agoOf course, but they don't know if the props come from a HOC or another "normal" component. If you're updating the render() of a component, you don't care where the props come from. It's not that important TBH, the abstraction is pretty thin
- ponyous 8y agoI'm writing TS components daily, I'm not sure why, but I never encountered any of the issues you mentioned. I can only agree that typing HOC was hard when I did it the first time.
- littlecranky67 8y agoThen let me ask you: How do you type .defaultProps? How do you type the "state" property on the class? If you did explicitly repeat the type already specified in the Generic Type Argument: Component<IProps, IState> you would know what I mean with too verbose (you have to repeat a type that could be infered, but due to TS restrictions isn't able to). If you did not repeat the type for those fields, you lost typesafety without noticing it. You can then go ahead and assign { foo: "bar" } to state (or use it in setState) and will not get a compilation error (which you shold as foo is not a known property for your state). And I sure as hell bet you do not use "strict" compilation in TS, which opens typing issues everywhere.
- ponyous 8y agoWe are using strict mode. If I do `setState({ undefinedStateKey: 123});` it warns me. Just checked. It may be linting not TypeScript. We have IState defined only as Generic Type Argument. We define defaultProps as static in class. And you are right defaultProps are not being type-checked against Generic Type Argument IProps.
- littlecranky67 8y agoWell if you are in strict mode, it should break the build with an error, not a warning. This seems to be a linting rule. But you see the issue with defaultProps and state here. You can basically assign anything to defaultProps or use in setState() even so you were explicit of the props type in the Generic Type Argument.
- deleted 8y ago[deleted]
- teniutza 8y agoSometimes I need to create the props at a different level/location and not directly on the component. The interface for the (class component) props is then very useful.
- littlecranky67 8y agoThis is no different when using Hooks. You specify the type of props as the type of the first argument in the functional component: function MyComponent(props: IMyComponentProps) { ... } Where the type definition of IMyComponentProps resides doesn't matter, you can import it from anywhere.
- teniutza 8y agoI was simply pointing out that explicitly defining an interface is not necessarily a downside.
- jameslk 8y ago> With Hooks you almost never need to explicitly type the type of state of a component [...] While when using class-based Components, you'd have to specify the state type as a generic argument You don't need to do this. You can accomplish the same thing with class syntax. Here's an example: class MyComponent extends Component<Props> { state = { inferredNum: 1, // TS will infer this to be a number explicitNullableString: null as string | null, // Tell TS this will be either null or a string } ... > And you'd have to use some kind of Partial<State> type when assigning only some values of the state in the property constructor. I can't say I've ever run into this problem with TS and React but maybe I'm not understanding what you're saying? Do you have an example? > Similar issues exist when using the static defaultProps field in TypeScript Here's what I do: defaultProps: Partial<Props> = {...} That doesn't seem such a big deal? > And if you ever tried to strictly type your HOCs or renderProps you know how hard it was, sometimes utilizing the conditional/mapped generic types feature of TS. This is completely gone with Hook. It wasn't hard to me but I can understand why others don't like generics. Hooks have their own learning curve, so you're trading one thing for another. > You'd have to override the non-null check using TS exclamation point operator. This has been fixed with TS 3 I believe.