[英]Optional Generic Based On Function Argument
我想通過 JSON 模式驗證我的 XHR 請求。 我有每種響應類型的驗證功能。 如果指定了驗證函數,則應從驗證函數中提取來自我的 XHR 處理程序的響應類型。 如果未指定驗證函數,我希望mixed
響應類型,以便必須處理未知的響應數據。
所以我有這個:
type HTTPMethod =
| 'GET'
| 'POST'
;
type ResponseValidator<Response> = (mixed) => Response;
type HTTPRequest<
Method: HTTPMethod,
Response: mixed,
> = {
url: string,
method: Method,
responseValidator?: ResponseValidator<Response>,
};
type GetRequest<Response = mixed> = HTTPRequest<'GET', Response>;
const defaultValidator: ResponseValidator<mixed> = (data: any) => (data: mixed);
const getRequest= <Response>({
url,
responseValidator = defaultValidator,
}: {
url: string,
responseValidator?: ResponseValidator<Response>,
}): GetRequest<Response> => ({
method: 'GET',
url,
responseValidator,
});
結果是:
23: responseValidator = defaultValidator,
^ mixed [1] is incompatible with `Response` [2].
References:
19: const defaultValidator: ResponseValidator<mixed> = (data: any) => (data:
mixed);
^ [1]
6: type ResponseValidator<Response> = (mixed) => Response;
^ [2]
我在想也許我可以在函數的Response
泛型上設置默認值,但 flow 似乎不支持函數泛型的默認值,我懷疑這是否真的有效。 有沒有更好的方法來解決這個問題?
我基本上回避了這個問題,從根本上說,我的類型更加明確。 所以現在我有兩種類型的請求構建器,一個RequestBuilder
:
/**
* Union of request builders for different HTTP methods.
*/
export type RequestBuilder<UrlParams, Params, SerializedParams> =
| GetRequestBuilder<UrlParams>
| DeleteRequestBuilder<UrlParams>
| PostRequestBuilder<UrlParams, Params, SerializedParams>
| HeadRequestBuilder<UrlParams, Params, SerializedParams>
;
和ValidatedRequestBuilder
(也許這應該是“validat荷蘭國際集團的要求建設者?”還有一些細節需要處理):
/**
* A RequestBuilder packaged up with a ResponseValidator and a deserializer.
*/
export type ValidatedRequestBuilder<
UrlParams,
Params,
SerializedParams,
RB: RequestBuilder<UrlParams, Params, SerializedParams>,
Response,
Format,
> = {
requestBuilder: RB,
responseValidator: ResponseValidator<Response>,
deserializer: (Response) => Format,
};
然后是這兩種類型的聯合, AbstractRequestBuilder
。 你會在這里看到這開始暗示解決方案:
/**
* A RequestBuilder which may or may not be a ValidatedRequestBuilder.
*
* This abstracts the API between RequestBuilder and ValidatedRequestBuilder so
* that they can be used interchangeable (this can be used as if it were a
* ValidatedRequestBuilder).
*/
export type AbstractRequestBuilder<
UrlParams,
Params,
SerializedParams,
RB: RequestBuilder<UrlParams, Params, SerializedParams>,
// it's very important that these default to `mixed` for a regular
// `RequestBuilder`, this behavior is relied upon when creating a default
// validator and deserializer for a regular `RequestBuilder`
Response=mixed,
Format=mixed,
> =
| ValidatedRequestBuilder<UrlParams, Params, SerializedParams, RB, Response, Format>
| RB;
所以就我們而言,所有請求構建器都是AbstractRequestBuilder
,所以當我們從有問題的AbstractRequestBuilder
實際構建請求時,如果底層請求構建器不是ValidatedRequestBuilder
,我們只需實現一個默認的驗證器和反序列化器它基本上是返回mixed
身份函數:
/**
* Gets a `ValidatedRequest` for the given `AbstractRequestBuilder`,
* `UrlParams`, and body `Params`.
*
* The important thing is that this does the job of differentiating between a
* `RequestBuilder` and a `ValidatedRequestBuilder` and abstracting behavior.
* Basically a `ValidatedRequestBuilder` will have a concrete `Response` and
* `Format`, while a `RequestBuilder` will end up with `mixed`.
*/
export const validatedRequestForBuilder = <
UrlParams,
Params,
SerializedParams: ValidParams,
Response,
Format,
ARB: AbstractRequestBuilder<UrlParams,
Params,
SerializedParams,
RequestBuilder<UrlParams, Params, SerializedParams>,
Response,
Format>,
>(
abstractRequestBuilder: ARB,
urlParams: UrlParams,
params: Params,
): ValidatedRequest<SerializedParams, Request<SerializedParams>, Response, Format> => (
typeof abstractRequestBuilder === 'function'
? {
request: (
abstractRequestBuilder: RequestBuilder<UrlParams,
Params,
SerializedParams>
)(urlParams, params),
responseValidator: data => ((data: any): Response), // Response is always mixed here
deserializer: (data: Response) => ((data: any): Format), // Format is always mixed here
}
: {
request: abstractRequestBuilder.requestBuilder(urlParams, params),
responseValidator: abstractRequestBuilder.responseValidator,
deserializer: abstractRequestBuilder.deserializer,
}
);
所以基本上每個請求構建器總是導致ValidatedRequest
s,它保證某些特定的反序列化響應類型,但在某些情況下,我們傳遞常規RequestBuilder
而不是ValidatedRequestBuilder
,特定的反序列化響應類型將是mixed
。 如果我們不想處理mixed
,那么我們應該指定一個驗證器。
因此,其核心是一個非常標准的模式,它涉及對類型進行良好和明確的處理,並使用聯合來建模替代場景,而不是像選項類型或可選屬性之類的東西。 工會更加明確。 我一直在思考這個問題,比如 react prop 類型。 你可能有這樣的事情:
type PriceType = 'wholesale' | 'retail';
type Props = {
label: string,
hasPrice: boolean,
priceType?: PriceType,
};
凡priceType
是必需的,如果hasPrice
是true
,但是是不相關的,如果hasPrice
是假的。 所以你看着它說,好吧,有時我會通過priceType
有時我不會,所以我想它應該是可選的。 但這實際上是兩個完全獨立的場景,需要聯合才能正確建模:
type PriceType = 'wholesale' | 'retail';
type AlwaysProps = $ReadOnly<{|
label: string,
|}>;
type Props = $ReadOnly<{|
...AlwaysProps,
hasPrice: true,
priceType: PriceType,
|}> | $ReadOnly<{|
...AlwaysProps,
hasPrice: false,
|}>;
所以我想這里的教訓是,當您發現自己使用選項時,您應該考慮這些選項是否可以或應該更准確地鍵入為聯合。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.