- 8 TypeScript Tips To Expand Your Mind (and improve your code)
- Resolve TypeScript Errors
- TS 5.3 Tips
- 8 Tips from the TS Wizard - using, as const, never
- 10 Tips for Mastering TypeScript Generics
- Turn MODULES INTO TYPES with typeof import
- Dynamic function arguments with GENERICS
- Blazing Fast Tips: React & TypeScript
- 13
importstatement - 12 Make a loose auto completed
- 8 Generic for React Components
Extendsin TypeScript- Tips with
constassertion - A hack for Generic in arrow function
- Retrieve from Generic params
- Type-safe on
Object.keys()
https://www.youtube.com/watch?v=QSIXYMIJkQg
Generic η±»εηδΈδΈͺεΎιθ¦ηη¨δΎε°±ζ―η±»εζ¨ζ(inference)οΌδΎε¦ Generic Components
https://www.youtube.com/watch?v=QSIXYMIJkQg&t=227s
https://www.youtube.com/watch?v=QSIXYMIJkQg&t=296s
// Given
declare function useStatus<T>(statuses: T[]): T;
// loadingStatus: string
const loadingStatus = useStatus(['loading', 'idle'])
// 0
// (T as const) run into syntax error
declare function useStatus<T>(statuses: T[]): (T as const);
// 1
// A 'const' assertions can only be applied to references to enum members, or string, number, boolean, array, or object literals.
function useStatus<T>(statuses: T[]) {return statuses[0] as const};
declare function useStatus<const T>(statuses: T[]): T;
// 2 works
// const loadingStatus: "loading" | "idle"
const loadingStatus = useStatus(['loading', 'idle'])https://www.youtube.com/live/8HoOxOd86M4?feature=share&t=338
interface O {
name: string;
school: string;
age: number;
}
type K = "name" | "school" | never;
// type X = "name" | "school"
// Filter `never` with `[keyof O]`
type V = {
[K in keyof O]: O[K] extends string ? K : never;
}[keyof O]https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints
https://www.youtube.com/watch?v=dLPgQRbVquo&t=499s
We need to actually constrain this O object because you can't call object.keys on a number, or on a string. So we actually need to constrain it to be something that object.keys can handle.
// This type parameter might need an `extends {}` constraint.
// This type parameter might need an `extends object` constraint.
const getKeys = <O>(obj: O) => Object.keys(obj);
// Resolved
const getKeys = <O extends Record<string, number>>(obj: O) => Object.keys(obj);When you have a generic function, you're usually going to put a constrain on it.
- This give you control over what user inputs.
- Less logic statements.
https://www.youtube.com/watch?v=dLPgQRbVquo&t=607s
// Type 'string[]' is not assignable to type '(keyof O)[]'.
// Type 'string' is not assignable to type 'keyof O'.
// Type 'string' is not assignable to type 'never'.
const getKeys = <O extends {}>(obj: O): Array<keyof O> => Object.keys(obj);
// Resolved
const getKeys = <O extends {}>(obj: O): Array<keyof O> =>
Object.keys(obj) as (keyof O)[];https://www.youtube.com/watch?v=dLPgQRbVquo&t=900s
NOT CLEAR YET
const makeZodSafeFetch = <TData>(
url: string,
schema: z.Schema<TData>
): Promise<TData> => {
return fetch(url)
.then((res) => res.json())
.then((res) => schema.parse(res));
};
const result = makeZodSafeFetch<Record<"firstName" | "lastName", string>>(
"",
{}
);https://www.youtube.com/watch?v=sswUBXaoXSI
// constants.js
export const ADD_TODO = "ADD_TODO";
export const REMOVE_TODO = "REMOVE_TODO";
export const EDIT_TODO = "EDIT_TODO";
// consumer.js
export type ActionModule = typeof import("./t1");
/**
* export const ADD_TODO = "ADD";
export const REMOVE_TODO = "REMOVE";
export const EDIT_TODO = "EDIT";
* export const actionModule: ActionModule = {
ADD_TODO: "ADD",
REMOVE_TODO: "REMOVE",
EDIT_TODO: "EDIT",
};
*/
export const actionModuleFoo: ActionModule = {
ADD_TODO: "ADD",
REMOVE_TODO: "REMOVE",
EDIT_TODO: "EDIT",
};
// type ActionKeys = "ADD_TODO" | "REMOVE_TODO" | "EDIT_TODO"
export type ActionKeys = keyof ActionModule;
// type Action = "ADD" | "REMOVE" | "EDIT"
export type Action = ActionModule[keyof ActionModule];https://www.youtube.com/watch?v=YE_3WwX-Dl8
type Evt =
| {
type: "SIGN_IN";
payload: {
userID: string;
};
}
| { type: "SIGN_OUT" };
// const sendEvent = (eventType: Evt["type"], payload?: any) => {}
declare function sendEvent<T extends Evt["type"]>(
...args: Extract<Evt, { type: T }> extends { payload: infer Payload }
? [type: T, payload: Payload]
: [type: T]
): void;
// Correct
sendEvent("SIGN_OUT");
sendEvent("SIGN_IN", { userID: "123" });
// Should Error
sendEvent("SIGN_OUT", {});
sendEvent("SIGN_IN", { userID: 123 });
sendEvent("SIGN_IN", {});
sendEvent("SIGN_IN");https://www.youtube.com/watch?v=37PafxU_uzQ
import { FocusEventHandler, FormEventHandler, MouseEventHandler } from "react";
interface ComponentProps {
onClick: MouseEventHandler<HTMLButtonElement>;
onFocus: FocusEventHandler<HTMLButtonElement>;
onBlur: FocusEventHandler<HTMLButtonElement>;
onChange: FormEventHandler<HTMLButtonElement>;
}import { ReactNode } from "react";
interface ComponentProps {
children?: ReactNode;
}import { useState } from "react";
interface User {}
const Name = () => {
// const name: User | undefined
const [name, setName] = useState<User π‘ no need `null` here>();
return null;
};import { useState } from "react";
const useHome = () => {
const [state, setState] = useState(0);
return [state, setState] as const; π
};
const Home = () => {
const [home, setHome] = useHome()
setHome(9)
return null;
}Supposing that confirmButtonMassage only belongs to confirm type. A union type can be used instead of an optional props indicator.
interface ModalProps {
type: "alert" | "confirm";
confirmButtonMassage?: string;
}
// π
type ModalPropsUnion =
| {
type: "alert";
}
| {
type: "confirm";
confirmButtonMassage: string;
};Discriminated type requires the type property to be assigned.
type Props =
| {
type?: "text";
}
| {
type?: "password";
visible: boolean;
};
declare function input(props: Props): void;
// `visible` will not report an error as lack of `type`
void input({ visible: true });
void input({ type: "password", visible: true }); // π―
void input({ type: "text", visible: true }); // π¨import React, { useState } from "react";
type TableProps<T> = {
data: T[];
onRowClick(row: T): void;
};
const Table = <T,>(props: TableProps<T>) => null;
const Parent = () => (
<Table
data={[{ name: "Foo" }]}
onRowClick={(row) => {
void row.name; π
}}
/>
);https://twitter.com/i/status/1508408811635322883
// ./constants.js
export const TODO_ADD = "ADD";
export const TODO_REMOVE = "REMOVE";
export const TODO_EDIT = "EDIT";
// ./index.js
// π
type Actions = typeof import("./constants");
// Actions will receive an object types.
const actions: Actions = {
TODO_EDIT: "EDIT",
TODO_ADD: "ADD",
TODO_REMOVE: "REMOVE",
};
// alias
// type Keys = "ADD" | "REMOVE" | "EDIT"
type Keys = Actions[keyof Actions];https://twitter.com/i/status/1506607945445949446
type Size = "xs" | "md";
type SizeOmitted = "xs" | "md" | Omit<string, "xs" | "md">;
// 'xs' | 'md' available for auto complete
const s1: SizeOmitted = "md";
// also works rather than the types explicitly defined
const s2: SizeOmitted = "something";
// step further with Generic Types
type LooseString<T extends string> = T | Omit<string, T>;
const s3: LooseString<Size> = "md";https://twitter.com/i/status/1503352924537339904
type HelloProps<T> = {
items: T[];
};
export function Hello<T>({ items }: HelloProps<T>) {
return (
<div>
{items.map((item) => (
<div key={String(item)}>{item}</div>
))}
</div>
);
}
export const SayHello = () => <Hello<{ items: string[] }> items={[1, 2]} />;const obj = {
foo: {
a: 1,
b: 2,
},
bar: {
c: 3,
d: 4,
}
}
declare const getDeepValue = <Obj, FirstKey extends keyof Obj, SecondKey extends keyof FirstKey>(obj: Obj, firstKey: FirstKey, secondKey: SecondKey) : Obj[FirstKey][SecondKey];
const result = getDeepValue(obj, 'foo', 'a');const useFirstName = () => {
const [firstName, setFirstName] = useState("");
return [firstName, setFirstName] as const π;
}
const createUser = () => ({
name: "Tsing",
role: "admin" as const π,
})https://www.youtube.com/watch?v=hBk4nV7q6-w&list=PLed0-rd1pwrdEcPWmwG50Pt_FLiEtWIu2&index=1&t=2132s
// π» the comma
const Table = <T,>(props: T) => null;const getDeepProperty = <
O,
FirstParam extends keyof O,
SecondParam extends keyof O[FirstParams]
>(obj: O, firstParam: FirstParam, secondParam: SecondParam) => {}const objKeys = <T extends object>(obj: T) => Object.keys(obj) as (keyof T)[];