let url: string = "https://google.com"
function convertCurrency(amount: number, currency: string) {
}
convertCurrency("blah", "US") // Error!
convertCurrency(5, "US") // Nice
export default function Button({
backgroundColor,
fontSize,
pillShape
}: {
backgroundColor: string;
fontSize: number;
pillShape: boolean;
})
type ButtonProps {
backgroundColor: string;
fontSize: number;
pillShape: boolean;
}
export default function Button({
backgroundColor,
fontSize,
pillShape
}: {
ButtonProps
})
هنا في مشكلة بقا! ازاي؟ لو حبيت بس اعمل button استخدم ال backgroundColor prop بس فيديني error ويطلب مني باقي ال props .. الحل هو ال optional! ودا شكلها:
type ButtonProps {
backgroundColor?: string;
fontSize?: number;
pillShape?: boolean;
}
طب انت حابب ت force بعض القيم .. يعني مثلا في ال backgroundColor حابب يكون م بين 3 خيارات فقط: red, blue or green تعملها ازاي؟
type ButtonProps {
backgroundColor?: "red" | "blue" | "green";
fontSize?: number;
pillShape?: boolean;
}
طب لو عندك أكتر من prop بيستخدم نفس القيم؟ هنا يجي دور ال variable:
type Color = "red" | "blue" | "green";
type ButtonProps {
backgroundColor?: Color;
textColor?: Color;
fontSize?: number;
pillShape?: boolean;
}
type ButtonProps {
backgroundColor?: Color;
padding: number[]
}
type ButtonProps {
backgroundColor?: Color;
border: [number, number, number, number, string]
}
// padding: [5, 10, 5, 10, "red"]
بدل م تفضل تعمل prop لكل property في css لا هتستخدم ال properties الموجودة في css بالفعل:
type ButtonProps = {
style: React.CSSProperties
}
export default function Button( { style }: ButtonProps ) {}
<Button
style={{
backgroundColor: "blue",
fontSize: 24,
color: "white",
...
}}
/>
تخيل معايا مثلا عاوز ال Button يستقبل prop إسمه borderRadius ويكون بالشكل دا:
<Button
borderRadius={{
'topLeft': 5,
'topRight': 10,
'bottomLeft': 5,
'bottomRight': 10,
}}
/>
في ال Button.tsx هتعملها ازاي؟ ممكن تقولي اعملها بالشكل دا:
type ButtonProps = {
borderRadius: {
topLeft: number;
topRight: number;
bottomLeft: number;
bottomRight: number;
}
}
export default function Button({ borderRadius }: ButtonProps) {
}
في طريقة افضل وهي ال Record
type ButtonProps = {
borderRadius: Record<string:number>
// key : value
}
type ButtonProps = {
onClick: () => void | number;
}
export default function Button({ onClick }: ButtonProps) {
}
type ButtonProps = {
children: React.ReactNode
}
export default function Button({ children }: ButtonProps) {
return <button>{children}</button>
}
Home.tsx:
export default function Home() {
const icon = <i></i>;
return (
<main>
<Button>{icon}</Button>
</main>
)
}
Button.tsx:
type ButtonProps = {
children: JSX.Element
}
export default function Button({ children }: ButtonProps) {
return <button>{children}</button>
}
لو حطيت text في ال button هيطلعلك error بيقولك لازم يكون JSX Element!
في فروق كتيرة بس علشان اختصر عليك استخدم type ومتستخدمش interface.
type InputProps = ComponentProps<"input">
const MyInput = (props: InputProps) => {
return <input {...props} className="my-input" />;
};
اي فايدتها؟ كل فكرتها بس إنها بتديك autocomplete لل component بتاعك بدل م تكتب وتخطئ.
Home.tsx:
export default function Home() {
return (
<main>
<Button type="submit" autoFocus={true} defaultValue="test" className="btn-primary"/>
</main>
)
}
Button.tsx:
type ButtonProps = React.ComponentPropsWithoutRef<"button">;
export default function Button({ type, autoFocus, ...rest }: ButtonProps) {
return <button type={type} autoFocus={autoFocus} {...rest}>{children}</button>
}
دي بإختصار هو جمع م بين 2 Types!
type ButtonProps = {
type: "button" | "submit" | "reset";
color: "red" | "blue" | "green";
}
type SuperButtonProps = ButtonProps & {
size: "md" | "lg";
}
interface ButtonProps {
type: "button" | "submit" | "reset";
color: "red" | "blue" | "green";
}
interface SuperButtonProps extends ButtonProps {
size: "md" | "lg";
}
type User = {
name: string;
age: number;
}
const [user, setUser] = useState<User | null>(null);
const name = user?.name;
const ref = useRef<HTMLButtonElement | null>(null);
const buttonTextOptions = [
"Click me!",
"CLick me again!"
] as const;
هتخلي ال array دي readonly يعني متقدرش تعدل عليها فقط تقرأ منها!
const arr = [1, 2, 3] as const;
arr.push(4);
// Error: Property 'push' does not exist on type 'readonly [1, 2, 3]'.
تخيل عندك 2 types مثلا User and Guest وال Guest ليه نفس خصائص ال User بس مثلا في property موجودة في ال User مش موجودة في ال Guest!
type User = {
sessionID: string;
name: string;
}
type Guest = Omit<User, "name">;
هنا ال Guest هياخد كل ال properties من ال User ما عدا ال user property!
type ButtonColor = "red" | "blue" | "green";
const prevButtonColor = localStorage.getItem("buttonColor") as ButtonColor;
بدون ال as ButtonColor
قيمة ال prevButtonColor كانت هتكون يا string يا null لكن دلوقتي بقت اي واحدة من الخيارات دي!
عندك function عاوزها تقبل اكتر من نوع؟ ممكن تقولي استخدم any بالشكل التالي:
const convertToArray = (value: any): any[] => {
return [value];
}
convertToArray(5)
convertToArray("test")
مفيش مشاكل صح؟ صح .. طب لو حبيت بس اضيف toUpperCase() بالشكل التالي:
const convertToArray = (value: any): any[] => {
return [value.toUpperCase()];
}
convertToArray(5)
convertToArray("test")
مش هيقول لأ بس تيجي تشغل الكود هيديك error! لإن ال number مفيهوش function بالأسم دا .. طب والحل؟ Generics
const convertToArray = <T,>(value: T): T[] => {
return [value.toUpperCase()];
}
convertToArray(5)
convertToArray("test")
هنا بتقول النوع ترجعله نوعه وطبعا T وتقدر تسميه K .. لكن دا المتعارف عليه .. ليه بقا ال comma اللي بعد ال T في <T,>
دي لإنك في ملف tsx والكومبايلر بدون ال comma ممكن يفكرك بتعمل component جديد! ممكن تحولها لفانكشن أفضل:
function convertToArray<T>(value: T): T[] {
return [value.toUpperCase()];
}
convertToArray(5)
convertToArray("test")
Button.tsx
type ButtonProps = {
countValue: number;
countHistory: number[];
}
لو جيت مثلا تستدعي ال Button Component دي بالشكل دا:
<Button countValue=5 countHistory=[10, 20, 30, 40]/>
لو جيت تغير نوع ال countValue ل string هيتغير عادي وتحط قيمة string عادي ودا كله ملوش علاقة بال countHistory .. طب انا عاوز الاتنين يكون م بينهم relationship في النوع الاتنين دايما يكونو نفس النوع اي الحل؟
type ButtonProps<T> = {
countValue: T;
countHistory: T[];
}
export default function Button<T>({
countValue,
countHistory
}: ButtonProps<T>) {
}
كدا انت خليت الاتنين يعتمدو علي بعض ويكونو من نفس النوع!
types.ts:
export type Color = "red" | "blue" | "green"
import { type Color } from "../lib/types.ts"
أفضل من any في حتة انك تستدعي API بس مش عارف اي اللي هيرجع منها! شوف دا علشان تعرف الفرق:
let value: any;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0][1]; // OK
let value: unknown;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
let value: unknown;
value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0][1]; // Error
const value: unknown = "Hello World";
const someString: string = value as string;
const otherString = someString.toUpperCase(); // "HELLO WORLD"
const value: unknown = 42;
const someString: string = value as string;
const otherString = someString.toUpperCase(); // BOOM
type UnionType1 = unknown | null; // unknown
type UnionType2 = unknown | undefined; // unknown
type UnionType3 = unknown | string; // unknown
type UnionType4 = unknown | number[]; // unknown
type UnionType5 = unknown | any; // any
type IntersectionType1 = unknown & null; // null
type IntersectionType2 = unknown & undefined; // undefined
type IntersectionType3 = unknown & string; // string
type IntersectionType4 = unknown & number[]; // number[]
type IntersectionType5 = unknown & any; // any
Without ts-reset
:
.json
(infetch
) andJSON.parse
both returnany
.filter(Boolean)
doesn't behave how you expectarray.includes
often breaks on readonly arrays
ts-reset
smooths over these hard edges, just like a CSS reset does in the browser.
With ts-reset
:
.json
(infetch
) andJSON.parse
both returnunknown
.filter(Boolean)
behaves EXACTLY how you expectarray.includes
is widened to be more ergonomic- And several more changes!