-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgeneric_type.ts
136 lines (101 loc) · 3.99 KB
/
generic_type.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
interface IDetails {
address: string;
}
function myFunc<Type extends IDetails>(arg: Type): Type {
console.log(arg.address); // Now we know it has a .address property, so no more error
return arg;
}
myFunc(true); // error: Argument of type 'boolean' is not assignable to parameter of type 'IDetails'
// ====================================================================================
const ObjectKeys1 = <T extends {}>(obj: T): Array<keyof T> => {
return Object.keys(obj) // Error: Type 'string[]' is not assignable to type '(keyof T)[]'.
// Array<keyof T> => ["id", "email"]
// Object.keys(obj) => ["id", "email"] --> string[]
}
const ObjectKeys2 = <T extends {}>(obj: T): Array<keyof T> => {
return Object.keys(obj) as Array<keyof T>
}
const result = ObjectKeys2({
id: 6,
email: "[email protected]"
})
// ====================================================================================
function getProperty1<T>(obj: T, key: keyof T) {
return obj[key];
}
let obj1 = { a: 1, b: "b", c: true, d: 4 };
getProperty1(obj1, "a") // return type: string | number | boolean
function getProperty2<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let obj2 = { a: 1, b: "b", c: true, d: 4 };
getProperty2(obj2, "a") // return type: number
// ====================================================================================
function test<T>(): void {
console.log('test fn')
}
const makeSet1 = <T>() => { // T not defined => it's unknown type
return test<T>()
}
const mySet1 = makeSet1() // makeSet1: <unknown>() => void ----------> defaultly is unknown because it's doesn't has default type
const makeSet2 = <T = number>() => { // T is number type defaultly
return test<T>()
}
const mySet2 = makeSet2() // makeSet2: <number>() => void ----------> defaultly is <number>
const mySet3 = makeSet2<string>() // makeSet2: <string>() => void ----------> set <string>
// ====================================================================================
type Base = {
id: string;
title: string;
};
type GenericSelectProps<TValue> = {
values: TValue[];
onChange: (value: TValue) => void;
};
export const GenericSelect = <TValue extends Base>({ values, onChange }: GenericSelectProps<TValue>) => {
const onSelectChange = (e) => {
const val = values.find((value) => value.id === e.target.value);
if (val) onChange(val);
};
return (
<select onChange= { onSelectChange } >
{
values.map((value) => (
<option key= { value.id } value = { value.id } >
{ value.title }
< /option>
))
}
< /select>
);
};
// This select is a "Book" type, so the value will be "Book" and only "Book"
<GenericSelect<Book> onChange={ (value) => console.log(value.author) } values = { books } />
// This select is a "Movie" type, so the value will be "Movie" and only "Movie"
<GenericSelect<Movie> onChange={ (value) => console.log(value.releaseDate) } values = { movies } />
export const AmazonCloneWithState = () => {
const [book, setBook] = useState<Book | undefined>(undefined);
const [movie, setMovie] = useState<Movie | undefined>(undefined);
return (
<>
<GenericSelect<Book> onChange= {(value) => setMovie(value)} values = { booksValues } />
<GenericSelect<Movie> onChange={ (value) => setBook(value) } values = { moviesValues } />
</>
);
};
// ====================================================================================
interface Animal {
name: string
}
interface Human {
firstName: string,
lastName: string
}
type ReturnFnType<T> = T extends Human ? { humanName: string } : { animalName: string }
export const getDisplayName = <TItem extends Animal | Human>(item: TItem): ReturnFnType<TItem> => {
if ("name" in item) return { animalName: item.name }
return { humanName: `${item.firstName} ${item.lastName}` }
}
const result1 = getDisplayName({ name: 'rabbit' });
const result2 = getDisplayName({ firstName: 'esrafil', lastName: 'elahi' });
const result3 = getDisplayName({ firstName: 'esra', lastName: 'elahi' });