ジェネリクスを簡単に理解する
ジェネリクスとは
ジェネリクスとは、一言でいうと型を動的にするものです。
動的にするとは
みなさんは普段、関数に引数を設けることで関数を動的にしています。
const f = (x) => typeof x;
f(1); // 'number'
f('hoge'); // 'string'
関数f
は引数にx
を取り、その値によって戻り値が動的に変化します。
TypeScriptの型においても、ジェネリクスを使うことでこれと同様のことを実現できます。
型を動的にする
関数の引数と同様に、ジェネリクスでは型の引数を設けることで型を動的にします。
const f = <T>(x: T): string => typeof x;
f<number>(1); // xはnumber型
<T>
が型の引数であり、T
の値によって関数の引数や戻り値が動的に変化します。
上記の例では、関数の呼び出し時にf<number>
としているので、f
はnumber型の引数x
を取り、戻り値がstring型の関数となります。
型変数のデフォルト値
型変数にはデフォルト値を設けることができます。関数の呼び出し時に型を指定しなければデフォルト値が代入され、型を指定すればその値が代入されます。
const f = <T = number>(x: T): string => typeof x;
f(1); // 型を指定していないのでxはデフォルト値のnumber型
f<string>('hoge'); // 型を指定しているのでxはstring型
型変数に代入できる型を制限する
{型変数} extends {代入を許可する型}
の形で型変数に代入できる型を制限することができます。
代入を許可する型
はユニオン型で複数指定可能です。
const f = <T extends string | number>(x: T): string => typeof x;
f<number>(1); // number型の代入は許可されている
f<boolean>(true); // boolean型の代入は許可されていないのでコンパイルエラーとなる
ちなみに型変数のデフォルト値と組み合わせるとこのようになります。
const f = <T extends string | number = string>(x: T): string => typeof x;
f(1); // デフォルト値はstring型なのでコンパイルエラーとなる
f<boolean>(true); // boolean型の代入は許可されていないのでコンパイルエラーとなる
実践編:コードリーディング
以上の知識を踏まえて、axios
のソースコードを読んでみます。
export class Axios {
get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
}
axios
のget
メソッドは、型引数を3つ取ります。
T
T
はデフォルト値がany
で、AxiosResponse
の型引数として渡されます。型引数をさらに別の型の型引数として渡しているということですね。(ややこしい)
AxiosResponse
の型定義を見てみるとdata
の型がT
となっています。つまりT
には、APIをコールして得たデータの型を代入すれば良いとわかります。
export interface AxiosResponse<T = any, D = any> {
data: T;
status: number;
statusText: string;
headers: AxiosResponseHeaders;
config: AxiosRequestConfig<D>;
request?: any;
}
R
R
はgetメソッドの戻り値であるPromiseの型(Promise<R>
)として渡されます。
また、R
にはAxiosResponse<T>
がデフォルト値として指定されています。通常はT
を使用してAPIをコールして得るデータの型を指定するので、R
をあえて指定することは少ないと思います。
D
D
もデフォルト値はany
で、AxiosRequestConfig
の型引数として渡されます。こちらもAxiosResponse
と同様で、D
は最終的にAxiosRequestConfig.data
の型となっていることがわかります。
export interface AxiosRequestConfig<D = any> {
url?: string;
method?: Method;
baseURL?: string;
transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[];
transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[];
headers?: AxiosRequestHeaders;
params?: any;
paramsSerializer?: (params: any) => string;
data?: D;
timeout?: number;
timeoutErrorMessage?: string;
withCredentials?: boolean;
adapter?: AxiosAdapter;
auth?: AxiosBasicCredentials;
responseType?: ResponseType;
xsrfCookieName?: string;
xsrfHeaderName?: string;
onUploadProgress?: (progressEvent: any) => void;
onDownloadProgress?: (progressEvent: any) => void;
maxContentLength?: number;
validateStatus?: ((status: number) => boolean) | null;
maxBodyLength?: number;
maxRedirects?: number;
socketPath?: string | null;
httpAgent?: any;
httpsAgent?: any;
proxy?: AxiosProxyConfig | false;
cancelToken?: CancelToken;
decompress?: boolean;
transitional?: TransitionalOptions;
signal?: AbortSignal;
insecureHTTPParser?: boolean;
}
Author And Source
この問題について(ジェネリクスを簡単に理解する), 我々は、より多くの情報をここで見つけました https://zenn.dev/flat_ito/articles/b278d1aa157a39著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Collection and Share based on the CC protocol