主頁 > .NET開發 > 在typescript中用spreadexpressions推斷一個通用元組

在typescript中用spreadexpressions推斷一個通用元組

2021-10-16 15:25:29 .NET開發

(TS版本--4.1)

我正在嘗試一個可以接受任何長度的陣列/元組的通用函式的型別;每個元素將有相同的屬性名稱,但可能的型別不同。

type Param<X = unknown, Y = unknown> = {
  x?: X
  y: Y
}

//回傳型別是一個固定的元組:對于每個元素,如果定義了就回傳x,否則回傳yfunction func(arg: Param[]/span>) {
  return arg. map(span class="hljs-params">elem => 'x'/span> in elem ? elem.x : elem.y )
} 

示例:

func([ {x: 'string', y: 123}。])
func([ {x: 'string', y: 123}, {x: 123, y: 'string' } ])

*update: 更新了函式,以澄清我為什么明確地關心X和Y:實際的回傳型別是一個元組,其各個元素可以是X或Y,這取決于原始引數

我真正想得到的是讓編譯器推斷每個引數的長度和元素型別,從而使結果被相應地型別化。

我已經嘗試了一些東西

我已經嘗試了一些東西。

  • 創建幾個助手來從Param<X,Y>
  • 中提取嵌套型別。
type InferX<T extends [...any[]] > = {
  [K in keyof T] : T[K] extends Param<infer X, any> ? X : never
}
type InferY<T extends [...any[]] > = {
  [K in keyof T] : T[K] extends Param<any, infer Y> ? Y : never
}
  • 給函式一些型別引數,以包括嵌套的型別,并將函式引數表示為一個傳播運算式,映射到這些通用型別
//this works when I provide explicit type parameters, but won't infer them implicitly from the arguments. assuming everything is `unknown` i.e. func<Param< unknown, unknown> [] , unknown[] , unknown[]> 
function func<
  T extends Param[] 。
  X extends { [K in keyof T] 。X[K] } = InferX<T> 。
  Y extends { [K in keyof T] 。Y[K] } = InferY<T> 。
>(
  arg: [...{ [K in keyof T]: Param<X[K], Y[K]> }] 。
) {
  return arg. map(span class="hljs-params">elem => 'x'/span> in elem? elem.x : elem.y ) as [
    ...{ [K in keyof T] 。T[K] extends {x: X[K]}? ? X[K] : Y[K] }
  ]
}

//application 1: works ok[/span
func<[{x: string, y: number}, {x: number, y: string}] > ([{x: 'string', y: 123}, {x: 123, y: 'string'}] )

//application 2: doesn't work - types are unknown 
func([{x: 'string', y: 123}] )

//當我試圖更明確地使用T型別時,它根本無法編譯--"其余元素型別必須是陣列型別。"在傳播引數型別上出現錯誤function func<
  T extends { [K in keyof T] 。Param<X[K], Y[K]> } & any[],
  X extends { [K in keyof T] 。X[K] } = InferX<T> 。
  Y extends { [K in keyof T] 。Y[K] } = InferY<T> 。
>(
  arg: [...{ [K in keyof T]: Param<X[K], Y[K]> }] // ERRORreturn arg. map(span class="hljs-params">elem => 'x'/span> in elem? elem.x : elem.y ) as [
    ...{ [K in keyof T] 。T[K] extends {x: X[K]}? ? X[K] : Y[K] }
  ]
}

想知道是否有人知道如何讓推理正確作業/我是否想錯了?

謝謝!

uj5u.com熱心網友回復:

為了獲得最好的結果,讓型別引數推理對編譯器來說盡可能簡單

當你呼叫一個泛型函式而沒有手動指定其型別引數時,編譯器需要推斷這些型別引數,通常是從作為引數傳遞給該函式的值中推斷出來的。假設我們有一個函式,它的呼叫簽名是

// type F<T> = .../span>
宣告 function foo<T>(x: F<T>) 。void;

然后我們這樣呼叫:

// declare const someValue: .../span>
foo(someValue)。

然后編譯器的作業是根據someValue的型別和F<T>的定義來確定T。 編譯器必須嘗試反轉 F,并從F<T>推匯出T。 這一點越簡單,推斷出的型別引數就越有可能符合你的期望。

在你的案例中,你有類似于

的東西
function func<
  T extends Param[] 。
  X extends { [K in keyof T] 。X[K] } = InferX<T> 。
  Y extends { [K in keyof T] 。Y[K] } = InferY<T> 。
>(
  arg: [...{ [K in keyof T]: Param<X[K], Y[K]> }] 。
): void

但是編譯器無法從一個型別為[...{ [K in keyof T]>的值中推斷出T, X, 和Y。Param<X[K], Y[K]> }]。 對編譯器來說,這實在是太多的型別操作了。 如果你做一些更簡單的事情,你會有更好的運氣,例如

span class="hljs-keyword">function func< T extends Param[]> (arg: T): void;

函式引數型別中的變數元組型別給了編譯器一個提示,以推斷元組而不僅僅是陣列

這樣做是可行的,但是編譯器傾向于推斷陣列型別而不是元組型別:

declare function func< T extends Param[]> (arg: T): void; func([{ x: ""/span>, y: 1 }, { x: 1, y: "" }]) // T推斷為陣列<{x: string, y: number}。| {x:數字,y:字串}>

由于你希望T被推斷為一個元組型別,而不僅僅是一個陣列型別,你可以使用變數元組型別來獲得這種行為,通過將arg: T改為arg: [...T]/code>或者arg: readonly [...T]/code>:

declare function func< T extends Param[]> (arg: [. ..T]): void;
func([{ x: ""/span>, y: 1 }, { x: 1, y: "" }]) 
//T推斷為[{ x: string; y: number;}, { x: number; y: string;}]

計算回傳型別:沒有注解,產生unknown[]

現在有一個問題是如何處理輸出型別。 它不是void。 讓我們研究一下示例代碼的實作:

function func< T extends Param[]> (arg: [. ..T]) {
  return arg。 map(span class="hljs-params">elem => 'x'/span> in elem ? elem.x : elem.y)
} 

如果我們不注釋回傳型別,它將被編譯器確定為unknown[];陣列的map()方法并不保留元組的長度,而且它當然不能遵循高階邏輯,為不同的索引產生不同的型別。

所以你會得到unknown[]

const p: Param<string, number> = (
  [{ x: ""/span>, y: 1 }, { y: 1 }, { x: undefined, y: 1 }] 。
)[Math.floor(Math.random() * 3) ]

const result = func([
  { x: ""/span>, y: 1 },
  { y: 1 },
  { x: undefined, y: 1 },
  p
])
// const result: unknown[]

這是真的,但對你來說是無用的。 由于編譯器不能推斷出map()的回傳值的強型別,我們將需要將其斷言為T的函式。 但是什么T的函式?

計算回傳型別:每個元素的xy屬性的聯合

好吧,對于每個數字索引I,你看元素T[I]并回傳其"x"屬性的型別T[I]["x"]或其"y"屬性的型別T[I]["y"]。 因此,第一種方法是回傳這些型別的union

//這里E是T陣列的某個元素。
type XorY<E> = E extends Param ? E["x"] | E["y"] : never;

function func<T extends Param[]>(arg: [...T]){
  return arg。 map(span class="hljs-params">elem => 'x'/span> in elem ? elem.x : elem.y) 
    { [I in keyof T] 。XorY<T[I]> }
}

const result = func([
  { x: ""/span>, y: 1 },
  { y: 1 },
  { x: undefined, y: 1 },
  p
])
// const result: [string | number, unknown, number | undefined, string | number | undefined]/span>

這就好了,我們有一個長度為4的元組,而且stringnumber型別大部分都在里面。 但是也有一些缺點。

一個缺點是,我們在使用的時候會出現一些問題。

一個缺點是編譯器沒有意識到,如果x存在,你肯定會得到它;估計對于result的第一個元素,型別應該是string,而不是string | number

另一個是,如果x知道存在的,型別unknown就會出來。 這實際上在技術上是正確的;編譯器不可能知道它推斷的{y: 1}意味著x屬性是明確缺失的。 {y: number}的型別并不意味著 "除了y之外沒有任何屬性存在";它只是意味著 "除了y之外沒有任何已知屬性存在"。 (也就是說,物件型別不是microsoft/TypeScript#12936中要求的那種 "精確"。) 所以編譯器決定T[I]['x']unknown,這就破壞了事情。 讓我們做一件技術上不正確但又方便的事情,說一個像{y: 1}意味著x缺失,因此我們只想讓y的型別出來。


計算回傳型別。x如果它存在,y如果它不存在,如果我們不知道,就計算聯合體

所以讓我們重新定義上面的XorY<E>型別,使它對陣列的元素型別E做以下分析:

  • 如果E有一個型別為X的非選擇的x屬性,我們應該回傳X
  • 如果E沒有x屬性,但是它有一個y型別的Y屬性,我們應該回傳Y
  • 否則,如果E有一個型別為X的可選x屬性和一個型別為Yy屬性,那么我們應該回傳X | Y

這應該涵蓋了所有的情況(當然也有可能是邊緣情況,所以你需要測驗)。

要準確地檢查x屬性是否是可選的有點困難;詳情請見本答案。 下面是一種寫XorY的方法:

type XorY<E> = E extends Param<any,infer Y> ? E['x'] extends infer X ?
  'x' extends keyof E ? {} extends Pick<E, 'x'/span>> ? X | Y : X : Y
  : 從來沒有 : 從來沒有

它以一種奇怪的方式計算XY作為E的函式;Y是通過條件型別推理找到的,但是對于X,我是直接索引到 E;這是因為如果你使用inferX將不包括undefined,但可選屬性有時是undefined(給或給--exactOptionalProperties編譯器標志)。所以當涉及到undefined時,E['x']infer更準確。

無論如何,用XY武裝起來,如果x是一個已知的鍵('x' extends keyof E),如果它是可選的({} extends Pick<E, 'x'>),我們回傳X | Y。 如果它是已知的但不是可選的,我們回傳X。 如果它是未知的,那么我們就回傳Y.

好吧,讓我們來試試:

const result = func( [ { x: ""/span>, y: 1 }, { y: 1 }, { x: undefined, y: 1 }, p ]) // const result: [string, number, undefined, string | number | undefined]/span> console.log(result) // [" ", 1, undefined, something]

完美! 這已經是我們所希望的最具體的了。 同樣,可能會有一些邊緣情況,你應該進行測驗以確保它在你的實際用例中發揮作用。 但我認為這是我能想象到的對所給示例代碼的最接近的解決方案。

游戲場地鏈接到代碼

uj5u.com熱心網友回復:

有一個替代版本:

type Elem = Param;

型別HandleFalsy<T extends Param> = (
  T['x'] extends undefined] ?
  ? T['y'] ?
  : (
    未知 extends T['x'] ?
    ? T['y'/span>] : T['x'/span>]
  )
)

Type ArrayMap<
  Arr extends ReadonlyArray<Elem> 。
  Result extends any[] = [] 。
  > = Arr extends [] 。
  ? Result ?
  : Arr extends readonly [infer H, ...infer Tail] ?
  ? Tail extends ReadonlyArray<Elem>
  ? H extends Elem> ?
  ? ArrayMap<Tail, [...Result, HandleFalsy< H> ]>
  : 絕不
  :永遠不會
  : 從來沒有。

型別 Param<X = unknown, Y = unknown> = {
  x?: X
  y: Y
}

型別 Json =
  | null =
  |字串
  | 數字
  | 布爾型
  | Array<JSON>
  | {
    [prop: string] 。Json
  }


const tuple = <
  X extends Json,
  Y extends Json,
  Tuple extends Param<X, Y> 。
  Arr extends Tuple[] 。
>(data: [...Arr]/span>) =>
  data.map(span class="hljs-params">elem => 'x'/span> in elem? elem.x : elem.y) as ArrayMap<Arr>

// [42, "only y"] 
const result1 = tuple([{ x: 42, y: 'str' }, { y: 'only y' }])

// [42, "only y", "100"]/span>
const result2 = tuple([{ x: 42, y: 'str' }, { y: 'only y' }, { x: '100', y: 999999 }])

Playground

為了更好地理解ArrayMapHandleFalsy中的內容,請看一下js表示法:

const HandleFalsy=(arg: Param)=> {
  if (!arg.x) {
    return arg.y y }
  }
  return arg.x }
}

const ArrayMap = (arr: ReadonlyArray<Elem>, result: any[] = [] )。) Array<Param[keyof Param]> => {
  if (arr.length == 0) {
    return result
  }

  const [head, ...tail] = arr;

  return ArrayMap(tail, [...result, HandleFalsy(head)] )

如果你想了解更多關于函式引數推理的資訊,你可以閱讀我的文章

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/318615.html

標籤:

上一篇:具有多個可選約束但只有一個通用引數的通用方法(c#)。

下一篇:使用泛型和單鏈表(錯誤CS0311)。

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more