简体   繁体   English

如何获取typescript 了解条件类型的使用

[英]How to get typescript to understand the use of a conditional type

I have a request handler for a post endpoint used to validate receipts.我有一个用于验证收据的 post 端点的请求处理程序。 The handler that takes one of two types as an incoming payload {location: 'foo' | 'bar', receipt: string | BarReceipt}将两种类型之一作为传入有效负载的处理程序{location: 'foo' | 'bar', receipt: string | BarReceipt} {location: 'foo' | 'bar', receipt: string | BarReceipt} {location: 'foo' | 'bar', receipt: string | BarReceipt} . {location: 'foo' | 'bar', receipt: string | BarReceipt} If location is foo then receipt is always string and visa versa, if location is bar then receipt is always BarReceipt .如果位置是foo那么收据总是string ,反之亦然,如果位置是bar那么收据总是BarReceipt I'm trying to determine the type of the payload based on a param within the payload and then have typescript understand what the type is without needing to cast it, but I cannot quite figure it out.我正在尝试根据有效负载中的参数确定有效负载的类型,然后让 typescript 了解类型是什么而无需强制转换,但我无法弄清楚。

// types.ts

export interface ValidateTransactionRequest extends Request {
  payload: ValidateTransactionPayload
}
export type ExtractLocation<T> = T extends {
  payload: { location: Location.foo }
}
  ? { payload: FooTransactionPayload }
  : T extends {
      payload: { location: Location.bar }
    }
  ? { payload: BarTransactionPayload }
  : never

export const enum Location {
  foo = 'foo',
  bar = 'bar',
}

export type ValidateTransactionPayload =
  | FooTransactionPayload
  | BarTransactionPayload

export interface FooTransactionPayload {
  location: Location.foo
  receipt: string
}
export interface BarTransactionPayload {
  location: Location.bar
  receipt: BarReceipt
}

export interface BarReceipt {
  orderId: string
}

This is the code where I want typescript to understand which type is in use.这是我希望 typescript 了解正在使用哪种类型的代码。

// handler.ts
async function handler<T extends ValidateTransactionRequest>(
    request: ExtractLocation<T>,
  ): Promise<VerificationResponse> {
    const { location, receipt } = request.payload

    if (location === Location.foo) {
       validateFooReceipt(receipt) // typescript warning type 'string | BarReceipt' is not assignable to type 'string'.
    } else if (location === Location.bar) {
      validateBarReceipt(receipt) // typescript warning type 'string | BarReceipt' is not assignable to type 'BarReceipt'.
    }
  }

function validateFooReceipt(receipt: string) {...}
function validateBarReceipt(receipt: BarReceipt) {...}

I can cast the type of the payload within each branch of the if statements and move on with my life, but I. Need.我可以在 if 语句的每个分支中转换有效负载的类型并继续我的生活,但我需要。 To.至。 Know.知道。

Sorry about leaving the bar receipts all over the place.很抱歉把酒吧收据留在了各处。

Answering my own question.回答我自己的问题。

It turns out that the destructuring the payload before the type guard is the issue here.事实证明,在类型保护之前解构有效负载是这里的问题。 See this thread on github https://github.com/microsoft/TypeScript/issues/13403请参阅 github https 上的此线程://github.com/microsoft/TypeScript/issues/13403

I put together an example in the typescript playground here .我在这里的 typescript 操场上放了一个例子。

type ReceiptRequest = FooReceiptRequest | BarReceiptRequest

interface BarReceiptRequest { payload: { location: 'bar', receipt: BarReceipt } }

interface FooReceiptRequest { payload: { location: 'foo', receipt: string } }

interface BarReceipt {
  foo: number
}

const handlerWorks = (request: ReceiptRequest) => {
  if (request.payload.location === 'foo') {
    takesAString(request.payload.receipt)
  } else if (request.payload.location === 'bar') {
    takesBarReceipt(request.payload.receipt)
  }
  // type Error when outside of the type guard :)
  takesBarReceipt(request.payload.receipt)
}

const handlerNotWorking = (request: ReceiptRequest) => {
const {receipt, location} = request.payload

if (location === 'foo') {
  // type error "Argument of type 'string | BarReceipt' is not assignable to parameter of type 'string'."
  takesAString(receipt)
  } else if (location === 'bar') {
    // type error "Argument of type 'string | BarReceipt' is not assignable to parameter of type 'BarReceipt'."
    takesBarReceipt(receipt)
  }
}

function takesAString(receipt: string) {}

function takesBarReceipt(receipt: BarReceipt) {}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM