Some lesser known TypeScript notation
4 min read
Exclamation Mark / Bang operator
I recently came across the following notation in the codebase I was working on, and got very confused about what the exclamation mark / bang operator was for, after userInfo
.
const initialFormValues: LocationData = {
currentCountry: props.route.params?.userData?.userInfo!.current_country_code ? 'x' : 'y',
};
I thought it was new Javascript notation at first, but subsequently found out that it's specifically for Typescript. Basically, in this case, props.route.params?
may or may not be present, hence the presence of the ?
. It follows on that userData
may therefore also not be present. However, if userData
is present, userInfo
will definitely be available, so it would not be correct to have userData?.userInfo?
. If you just leave it as props.route.params?.userData?.userInfo.current_country_code
however, the Typescript compiler will complain about userInfo
potentially being undefined. So what to do?
Enter the bang operator. By adding !
after userInfo
, you're telling Typescript to stop complaining because you're explicitly saying that it cannot be null
or undefined
here. Thanks for trying to guard us Typescript, but we know better in this case. 😉
Casting as "unknown"
The second Typescript notation that I recently learnt about was resetting types by casting as "unknown". What does this mean? Here's a concrete example:
Let's assume I have a Formik form. If you've not heard of Formik before, it's a very popular form library that's widely used in React and React Native apps (check out my other blog posts on Formik if you're interested). The nice thing about Formik is that it comes nicely typed and plays very nicely with a Typescript app. The other nice thing is that Formik works by effectively managing your form's state for you in such a way that you can easily abstract out your various form components and use Formik's context to access things like form values across different components.
So in the code example below, I have a Formik
wrapper component, which has 3 different input fields and a submit button. I've abstracted out the 3 input fields into their own components, for MechanismQuestion
, DateQuestion
and LocationQuestion
.
As I'll be running validation checks within each field component, I need to access the Formik state to see what the form values are, and therefore need to pass formikProps
as a prop into each component (essentially passing down the current Formik form values that are in state into each component).
export interface MechanismData {
mechanism: string;
}
export interface DateData {
date: Date;
}
export interface LocationData {
location: string;
}
<Formik
// ... add Formik config here
>
{(props) => { // props is provided by Formik
return (
<Form>
<MechanismQuestion
formikProps={props as unknown as FormikProps<MechanismData>}
/>
<DateQuestion formikProps={props as unknown as FormikProps<DateData>} />
<LocationQuestion
formikProps={props as unknown as FormikProps<LocationData>}
/>
<Button>Submit</Button>
</Form>
)
}}
</Formik>
The formikProps
contains all of the form's current values i.e. the value of mechanism, date and location. However, if say we just considered LocationQuestion
, this component really only cares about location and I therefore want to strictly type this component to only accept the form values that pertain to location.
Simple right? Let's just recast props
to a new type!
Unfortunately not so simple - if I had done something like the below, Typescript would have complained because props
already has its defined type from Formik (containing everything in Formik's state) and therefore thinks we've made a mistake in wanting to take on another custom type of LocationData
.
<LocationQuestion
formikProps={props as FormikProps<LocationData>}
/>
What we therefore need to do in this case, is to recast props
as unknown
, before then recasting again into the specific type we want. What this does is to essentially erase the currently set type for props
, so that we can happily define our new type for it.
<LocationQuestion
formikProps={props as unknown as FormikProps<LocationData>}
/>