You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Some Sets are infinite: string, object; some finite: boolean, undefined.
unknown is Universal Set (including all values), while never is Empty Set (including no value).
The & operator creates an Intersection. It creates a smaller set. It must have both.
The | operator creates a Union: a larger Set but potentially with fewer commonly available fields (if two object types are composed).
2. Understand declared type and narrowed type
A variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type. You can narrow a type by checking values.
3. Use a discriminated union instead of optional fields
constallowedChildren=["string","span","em","b","i","strong"];functionisSupportedElement(child: React.ReactElement){return(allowedChildren.some((c)=>c===child.type)||ReactIs.isFragment(child));}// Only certain child elements are accepted. Recursively check child elements to assure all elements are supported.functionvalidateChildren(children: React.ReactNode){returnReact.Children.map(children,(child)=>{if(!React.isValidElement(child))returnchild;constelementChild: React.ReactElement=child;if(child.props.children)validateChildren(elementChild.props.children);if(!isSupportedElement(elementChild)){thrownewError(`Children of type ${child.type} aren't permitted. Only the following child elements are allowed in Inline Alert: ${allowedChildren.join(", ")}`);}returnelementChild;});}
React's ref has 2 types: React.ObjectRef (which has a current prop, and is normally what you'll want), and React.RefCallback which is for callback functions. The shorter, React.Ref is the broader type that covers both, so that's rarely useful.
Prefer unknown over any. How to read: unknown is I don’t know. any is I don’t care. So we should favor saying “I don’t know”, rather than “I don’t care”. Because “I don’t know” means, when you work with this, you need to narrow the type.
"Extend" without an interface using an intersection type. But AVOID THIS because it's slow:
exporttypeButtonProps=React.ButtonHTMLAttributes<HTMLButtonElement>&{/** Adds a class to the root element of the component */className?: string;}
Strongly typed keys via Object.keys
// Helper to get strongly typed keys from object via https://stackoverflow.com/questions/52856496/typescript-object-keys-return-stringconstgetKeys=Object.keysas<Textendsobject>(obj: T)=>Array<keyofT>;
Default Props and destructuring
typeLoginMsgProps={name?: string;};functionLoginMsg({ name ="Guest"}: LoginMsgProps){return<p>Loggedinas{name}</p>;}
Think of this as "whenever something has the type "IconName", it must be a string that matches one of the keys of the icons object." More on why this works.
Declare a prop on a native HTML element as required
typeButtonProps=Omit<JSX.IntrinsicElements["button"],"type">;functionButton({ ...allProps}: ButtonProps){return<buttontype="button"{...allProps}/>;
}// 💥 This breaks, as we omitted typeconstz=<Buttontype="button">Hi</Button>;
interfaceCommonProps{children: React.ReactNode// ...other props that always exist}typeTruncateProps=|{truncate?: false;showExpanded?: never}|{truncate: true;showExpanded?: boolean}typeProps=CommonProps&TruncatePropsconstText=({ children, showExpanded, truncate }: Props)=>{// Both truncate & showExpanded will be of// the type `boolean | undefined`}
// IMPORTANT: Import this file BEFORE the thing you want to mock.import*asfetchModulefrom"node-fetch";jest.mock("node-fetch");constfetchModuleDefault=fetchModule.defaultasunknownasjest.Mock<any>;// Disables console logging. Useful for tests that call// code that outputs to the console, so we don't litter the test// output with needless console statements.exportfunctiondisableConsoleLog(){jest.spyOn(console,"warn").mockImplementation();jest.spyOn(console,"info").mockImplementation();jest.spyOn(console,"log").mockImplementation();jest.spyOn(console,"error").mockImplementation();}beforeEach(()=>{fetchModuleDefault.mockClear();});afterEach(()=>{jest.restoreAllMocks();});// Useful for complete controlexportconstmockFetch=(fn: any=()=>null)=>{fetchModuleDefault.mockImplementation(jest.fn(fn));returnfetchModuleDefault;};typeMockFetchResponse={responseJson?: any;status?: number;};// Convenient when you just want to specify the responseexportconstmockFetchResponse=({
responseJson ="",
status =200,}: MockFetchResponse)=>{returnmockFetch(async()=>({ok: status>=200&&status<300,
status,json: async()=>responseJson,}));};// Via https://instil.co/blog/typescript-testing-tips-mocking-functions-with-jest/exportfunctionmockFunction<Textends(...args: any[])=>any>(fn: T): jest.MockedFunction<T>{returnfnasjest.MockedFunction<T>;}
Template literal types - Useful when you want to use a template string with some placeholders to declare a union of string literal types. Yes, you could generate the list manually (and you should if the list is huge), but this can be handy when the size is reasonable.
10 JS/TS features I avoid
Tips
1. Think in sets.
2. Understand declared type and narrowed type
3. Use a discriminated union instead of optional fields
Above, the kind property lets us discriminate. This gives us more type safety than merely using optional fields.
4. Use a type predicate to avoid type assertion
tsconfig settings checklist for ideal safety
allowJs
falseKey Blog posts, tweets, sites
!
is a smellnumber
orstring
. And easy with Zod via .brand. Also see ts-brandAvoid Enums
Prefer string literal types or const objects over enums. String literal types video
d.ts files
Handling Boundaries
Boundaries:
-Local storage
-User input
-Network
-Config-based or Conventions
-File system
-Database requests
Ways to handle boundaries
Convert a large existing project from JS to TS via a script:
https://github.com/airbnb/ts-migrate
Cheat sheets
Utility libraries
Pattern matching via ts-pattern
type-fest
ts-extras
tiny-invariant - Throw an error if something unexpected occurs.
TypeScript with Node
Boilerplates:
swc with Node boilerplate
https://github.com/reconbot/typescript-library-template
Generators for mocking/ testing
Books
Websites / Repos
https://github.com/mdevils/typescript-exercises
TypeScript Tips
Validate children
Map JSON strings to native JavaScript dates
Type your data as close to the source as possible, or better yet, use Zod to validate if on the server (Zod is a bit heavy for the client). Zod is especially helpful for validating and Zod schemas can easily be converted into TS types via
infer
. My Sandbox with a Zod format functionStrongly type env vars using Zod. And use types to disable process.env
React's ref has 2 types:
React.ObjectRef
(which has a current prop, and is normally what you'll want), andReact.RefCallback
which is for callback functions. The shorter,React.Ref
is the broader type that covers both, so that's rarely useful.Prefer
unknown
overany
. How to read:unknown
is I don’t know.any
is I don’t care. So we should favor saying “I don’t know”, rather than “I don’t care”. Because “I don’t know” means, when you work with this, you need to narrow the type.Why I don't use React.FC
"Extend" without an interface using an intersection type. But AVOID THIS because it's slow:
Strongly typed keys via Object.keys
Default Props and destructuring
WithChildren helper type:
Clone
React.ReactNode
Support spreading props or using a computed property
Use a type to declare a union type
Given a type like this
You can declare a type like this:
Think of this as "whenever something has the type "IconName", it must be a string that matches one of the keys of the icons object." More on why this works.
Declare a prop on a native HTML element as required
First, create a helper:
Then, use it:
Remove a prop and declare it differently
Spread attributes to HTML elements
Omit a type
Conditional React Props (Use TS to only allow specific combinations of related props) - And an excellent video
Single onChange handler
Derive array from union
Testing via Jest
Test utils:
Template literal types - Useful when you want to use a template string with some placeholders to declare a union of string literal types. Yes, you could generate the list manually (and you should if the list is huge), but this can be handy when the size is reasonable.
Advanced TypeScript
The pivotal moment of transitioning from intermediate to advanced TypeScript was realizing the type system is a programming language in itself, w/ variables, functions, conditionals, & loops. Utility types like Required, Record, Pick, Omit are all built with these primitives.
Generics parameterize types like functions parameterize value. Generics overview from Matt
This table is a summary of Type Programming
extends
with ternaryas
withnever
infer
The text was updated successfully, but these errors were encountered: