Nick Fisher / SoundCloud / @spadgos
fn = (x: 42) => x;
fn = (x: 'hi') => x;
const arrOfStrings: string[] = [];
const arrOfStrings: Array<string> = [];
// using generics in a function signature
function identity<T>(x: T): T {
return x;
}
// using generics in an interface
interface Resource<T> {
id: T;
}
interface Person {
firstName: string;
lastName: string;
}
function getFullName(person: Person): string {
return person.firstName + ' ' + person.lastName;
}
const person: Person = {
firstName: 'Grace',
lastName: 'Hopper'
};
const name = getFullName(person);
new User(data)
)
makeRequest(`users/${id}/tracks?limit=${limit}`)
.then(parseResponse);
function parseResponse(data: any): Array<Track> {
return data;
}
"Trust me"
const endpoints = {
userTracks: {
url: '/users/:id/tracks',
query: {
limit: 10
}
},
trackStats: {
url: '/tracks/:id/stats/:category'
}
}
Endpoints.callEndpoint(
'userTracks', { id: 123 }, { limit: 5 }
).then( ... );
Endpoints.callEndpoint(
'userTracks', { id: 123 }, { limit: 5 }
).then( ... );
type EndpointsInstance = {
callEndpoint: (
name: 'userTracks',
pathParams: { id: number },
queryParams: { limit?: number }
) => Promise<Array<Track>>
};
↓ Demo
type EndpointsInstance = {
callEndpoint: ((
name: 'userTracks',
pathParams: { id: number },
queryParams: { limit?: number }
) => Promise<Array<Track>>)
& ((
name: 'trackStats',
pathParams: {
id: number,
category: 'plays' | 'likes' | 'reposts'
},
queryParams: {}
) => Promise<Stats>)
};
↓ Demo
Problem: Tooling support not great
Solution: Partial application!
(name: string, pathParams: object, queryParams: object) => Promise;
(name: string) => (pathParams: object, queryParams: object) => Promise;
callEndpoint('user')({ id: 123 }, {}).then((user) => ...)
Problem: Repetitive
Solution: Generics to the rescue!
type Endpoint<N, P, Q, R>
= (name: N) => (pathParams: P, queryParams: Q) => Promise<R>
type EndpointsInstance = {
callEndpoint:
Endpoint<
'userTracks',
{ id: number }, { limit?: number },
Array<Track>
>
& Endpoint<
'trackStats',
{
id: number,
category: 'plays' | 'likes' | 'reposts'
}, {}, Stats
>
};
// search-endpoints.ts
export Users = Endpoint<'searchUsers', null, { query: string }, User[]>
export Tracks = Endpoint<'searchTracks', null, { query: string }, Track[]>
// user-endpoints.ts
export User = Endpoint<'user', { id: number }, {}, User>
import * as Search from './search-endpoints';
import * as User from './user-endpoints';
type EndpointsInstance = {
callEndpoint: Search.Users & Search.Tracks & User.User;
}
↓ Demo
"Trust me"
One way to verify a type def... use TS itself
function verifyUser(data: User) {}
verifyUser({ id: 123, firstName: 'Ada' });
verifyUser({ id: 11, firstName: 11 }); // error
function verify<R>(data: R): void {}
Grab some fixtures, away you go
...but don't forget to maintain them
callEndpoint('user')({ id: 123 }, {}); // => Promise<User>
Don't we have everything we need here?
verify('user')({ id : 123 }, {})({
id: 123,
firstName: 'Ada'
});
callEndpoint:
(name: N) => (path: P, query: Q) => Promise<R>
verify:
(name: N) => (path: P, query: Q) => (expectedResult: R) => void;
Now, just put the right fixture with the right call
type CallUser = Endpoint<'user', { id: number }, {}, User>
type VerifyUser = Verify<'user', { id: number }, {}, User>
type Endpoint<N, P, Q, R> = (name: N) => (path: P, query: Q) => Promise<R>
type Endpoint<N, P, Q, R> = {
call: (name: N) => (path: P, query: Q) => Promise<R>
verify: (name: N) => (path: P, query: Q) => (result: R) => void;
}
type EndpointsInstance = {
callEndpoint: Search.Tracks['call']
& Search.Users['call'] & ... // etc
}
type Verify = Search.Tracks['verify']
& Search.Users['verify'] & ... // etc
Remember what we're building here...
verify('user')({ id : 123 }, {}); // (User) => void
callEndpoint('user')({ id : 123 }, {}); // Promise<User>
We can test the library using the library itself
verify('user')({ id: 123 }, {});
verify('user')({ id: 234 }, {});
verify
calls
verify('user')({id: 123}, {})({ id: 123, firstName: 'Ada' });
verify('user')({id: 234}, {})({ id: 234, firstName: 'Hedy' });
tsc
on the modified code