日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

typescript_如何掌握高级TypeScript模式

發(fā)布時(shí)間:2023/11/29 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 typescript_如何掌握高级TypeScript模式 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

typescript

by Pierre-Antoine Mills

皮埃爾·安托萬·米爾斯(Pierre-Antoine Mills)

如何掌握高級TypeScript模式 (How to master advanced TypeScript patterns)

了解如何為咖喱和Ramda創(chuàng)建類型 (Learn how to create types for curry and Ramda)

Despite the popularity of currying and the rise of functional programming (and of TypeScript), it is still a hassle today to make use of curry and have proper type checks. Even famous libraries like Ramda do not provide generic types for their curry implementations (but we will).

盡管currying的流行和函數(shù)式編程(以及TypeScript)的興起,但如今使用curry并進(jìn)行適當(dāng)?shù)念愋蜋z查仍然很麻煩。 即使像Ramda這樣的著名圖書館也沒有為其咖喱實(shí)現(xiàn)提供通用類型(但我們會提供)。

However, you need no functional programming background to follow this guide. The guide is about currying but it is only a topic of my choice to teach you advanced TypeScript techniques. You just need to have practised a bit with TypeScript’s primitive types. And by the end of this walk-through, you will be a real TS wizard ?.

但是,您不需要功能性編程背景即可閱讀本指南。 該指南是關(guān)于currying的,但這只是我選擇教高級TypeScript技術(shù)的主題。 您只需要對TypeScript的原始類型進(jìn)行一些練習(xí)即可。 在本演練結(jié)束時(shí),您將成為真正的TS向?qū)?#xff1f;

If you’re a functional programmer, you are probably already using currying to create powerful compositions and partial applications… And if you are a bit behind, it’s time to take the leap into functional programming, start shifting away from the imperative paradigm and solve problems faster, with ease, and promote reusability within your codebase.

如果您是函數(shù)式程序員,則可能已經(jīng)在使用curring來創(chuàng)建功能強(qiáng)大的合成和部分應(yīng)用程序……如果您有些落后,是時(shí)候邁入函數(shù)式編程,開始遠(yuǎn)離命令式范式并解決問題了更快,更輕松地實(shí)現(xiàn)代碼庫中的可重用性。

At the end of this guide, you will know how to create powerful types like:

在本指南的最后,您將了解如何創(chuàng)建強(qiáng)大的類型,例如:

In fact, Ramda does have some kind of mediocre types for curry. These types are not generic, hard-coded, limiting us to a certain amount of parameters. As of version 0.26.x, it only follows a maximum of 6 arguments and does not allow us to use its famous placeholder feature very easily with TypeScript. Why? It’s hard, but we agree that we had enough and we’re going to fix this!

實(shí)際上,Ramda確實(shí)有一些普通的咖喱類型。 這些類型不是通用的, 硬編碼的 ,將我們限制為一定數(shù)量的參數(shù)。 從0.26.x版本開始,它最多只能使用6個(gè)參數(shù) ,并且不允許我們通過TypeScript輕松使用其著名的占位符功能。 為什么? 很難,但是我們同意我們已經(jīng)足夠了,我們將解決這個(gè)問題!

什么是咖喱? (What is currying?)

But before we start, let’s make sure that you have a very basic understanding of what currying is. Currying is the process of transforming a function that takes multiple arguments into a series of functions that take one argument at a time. Well that’s the theory.

但是,在開始之前,請確保您對什么是curry有一個(gè)非常基本的了解。 咖喱化是將一個(gè)包含多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換為一系列同時(shí)包含一個(gè)參數(shù)的函數(shù)的過程。 嗯,這就是理論。

I prefer examples much more than words, so let’s create a function that takes two numbers and that returns the result of their addition:

我更喜歡示例而不是單詞,因此讓我們創(chuàng)建一個(gè)接受兩個(gè)數(shù)字并返回加法結(jié)果的函數(shù):

The curried version of simpleAdd would be:

curry版本的simpleAdd將是:

In this guide, I will first explain how to create TypeScript types that work with a standard curry implementation.

在本指南中,我將首先說明如何創(chuàng)建與標(biāo)準(zhǔn)curry實(shí)現(xiàn)一起使用的TypeScript類型。

Then, we will evolve them into more advanced types that can allow curried functions to take 0 or more arguments.

然后,我們會將它們演化為更高級的類型 ,這些類型可以使咖喱函數(shù)接受0個(gè)或多個(gè)參數(shù)。

And finally, we will be able to use “gaps” that abstract the fact that we are not capable or willing to provide an argument at a certain moment.

最后,我們將能夠使用“空白”來抽象化我們在某一時(shí)刻不具備或不愿提供論據(jù)的事實(shí)。

TL;DR: We will create types for “classic curry” & “advanced curry” (Ramda).

TL; DR :我們將為“經(jīng)典咖喱”和“高級咖喱”(拉姆達(dá))創(chuàng)建類型。

元組類型 (Tuple types)

Before we start learning the most advanced TypeScript techniques, I just want to make sure that you know tuples. Tuple types allow you to express an array where the type of a fixed number of elements is known. Let’s see an example:

在我們開始學(xué)習(xí)最先進(jìn)的TypeScript技術(shù)之前,我只想確保您了解tuple 。 元組類型使您可以在已知固定數(shù)量元素類型的情況下表示一個(gè)數(shù)組。 讓我們來看一個(gè)例子:

They can be used to enforce the kind of values inside a fixed size array:

它們可用于在固定大小的數(shù)組中強(qiáng)制使用值的種類:

And can also be used in combination of rest parameters (or destructuring):

并且還可以結(jié)合使用其余參數(shù)(或解構(gòu)):

But before starting to build our awesome curry types, we’re going to do a bit of a warmup. We are going to create the first tools that we need to build one of the most basic curry types. Let’s go ahead.

但是在開始建立很棒的咖喱類型之前,我們將做一些熱身。 我們將創(chuàng)建構(gòu)建最基本的咖喱類型之一所需的第一個(gè)工具。 讓我們繼續(xù)。

Maybe you could guess… We are going to work with tuple types a lot. We’ll use them as soon as we extracted the parameters from the “original” curried function. So for the purpose of an example, let’s create a basic function:

也許您會猜到……我們將大量處理元組類型。 從“原始”咖喱函數(shù)中提取參數(shù)后,我們將立即使用它們。 因此,出于示例的目的,讓我們創(chuàng)建一個(gè)基本函數(shù):

We extracted the parameter types from fn00 thanks to the magic of Parameters. But it’s not so magical when you recode it:

fn00 Parameters的魔力,我們從fn00提取了參數(shù)類型。 但是當(dāng)您重新編碼時(shí),它并不是那么神奇:

Let’s test it:

讓我們測試一下:

Good, it works just as Parameters does. Don’t be scared of infer, it is one of the most powerful keywords for building types. I will explain it in more detail right after we practiced some more:

很好,它的工作方式與Parameters一樣。 不要害怕infer ,它是構(gòu)建類型最強(qiáng)大的關(guān)鍵字之一。 在我們進(jìn)行更多實(shí)踐之后,我將更詳細(xì)地解釋它:

Earlier, we learnt that a “classic curried” function takes one argument at a time. And we also saw that we can extract the parameter types in the form of a tuple type, very convenient. So Head takes a tuple type T and returns the first type that it contains. This way, we’ll be able to know what argument type has to be taken at a time.

較早之前,我們了解到“經(jīng)典咖喱”函數(shù)每次僅接受一個(gè)參數(shù)。 而且我們還看到我們可以以元組類型的形式提取參數(shù)類型,非常方便。 所以Head接受一個(gè)元組類型T并返回它包含的第一個(gè)類型 。 這樣,我們就能知道一次必須采用哪種參數(shù)類型。

Let’s test it:

讓我們測試一下:

尾巴 (Tail)

A “classic curried” function consumes arguments one by one. This means that when we consumed the Head<Params&lt;F>>, we somehow need to move on to the next parameter that hasn’t been consumed yet. This is the purpose of Tail, it conveniently removes the first entry that a tuple might contain.

“經(jīng)典咖喱”函數(shù)逐個(gè)使用參數(shù)。 這意味著當(dāng)我們使用Head<Params& lt; F >>時(shí),我們需要以某種方式移動到尚未使用的ne xt參數(shù)。 這是pur尾的姿態(tài),它方便地刪除第一個(gè)條目的元組可能包含。

As of TypeScript 3.4, we cannot “simply” remove the first entry of a tuple. So, we are going to work around this problem by using one very valid trick:

從TypeScript 3.4開始,我們不能“簡單地”刪除元組的第一個(gè)條目。 因此,我們將使用一個(gè)非常有效的技巧來解決此問題:

Using function types, we were able to tell TypeScript to infer the tuple that we wanted. If you do not understand it yet, it is not a problem, this is just a warmup, remember?

使用函數(shù)類型 ,我們能夠告訴TypeScript推斷我們想要的元組。 如果您還不了解它,那不是問題,這只是預(yù)熱,還記得嗎?

Let’s test it:

讓我們測試一下:

HasTail (HasTail)

A curried function will return a function until all of it’s parameters have been consumed. This condition is reached when we called Tail enough times that there is no tail left, nothing’s left to consume:

直到它的所有的參數(shù)都已經(jīng)消耗了咖喱函數(shù)會返回一個(gè)函數(shù)。 當(dāng)我們調(diào)用Tail的次數(shù)足夠多,以至于沒有尾巴,沒有東西可消耗時(shí),便達(dá)到了這種條件:

Let’s test it:

讓我們測試一下:

重要關(guān)鍵字 (Important keywords)

You have encountered three important keywords: type, extends and infer. They can be pretty confusing for beginners, so these are the ideas they convey:

您遇到了三個(gè)重要的關(guān)鍵字: typeextendsinfer 。 對于初學(xué)者來說,它們可能會讓人很困惑,所以這些是他們傳達(dá)的想法:

  • extends:

    extends

    To keep it simple, you are allowed to think of it as if it was our dear old

    為簡單起見,您可以考慮一下它,就像它是我們親愛的古老

    JavaScript’s

    JavaScript的

    ===. When you do so, you can see an extends statement as a simple ternary, and then it becomes much simpler to understand. In this case, extends is referred to as a conditional type.

    === 。 這樣做時(shí),您可以將extends語句視為簡單的三元 ,然后它變得更易于理解。 在這種情況下, extends被稱為條件類型

  • type:

    type

    I like to think of a type as if it was a

    我喜歡認(rèn)為一種類型

    function, but for types. It has an input, which are types (called generics) and has an output. The output depends on the “l(fā)ogic” of a type, and extends is that block of logic, similar to an if clause (or ternary).

    功能 ,但適用于類型。 它有一個(gè)輸入(類型)(稱為Generic ),并有一個(gè)輸出。 輸出取決于類型的“邏輯”,并且extends到該邏輯塊,類似于if子句(或三進(jìn)制)。

  • infer:

    infer

    It is the magnifying glass of TypeScript, a beautiful inspecting tool that can

    它是TypeScript的放大鏡,是一種漂亮的檢查工具,可以

    extract types that are trapped inside different kinds of structures!

    提取陷在不同類型結(jié)構(gòu)中的類型!

I think that you understand both extends & type well and this is why we are going to practice a bit more with infer. We’re going to extract types that are contained inside of different generic types. This is how you do it:

我認(rèn)為您對extends和type都理解得很好,這就是為什么我們要在infer多做一些練習(xí)的原因。 我們將提取包含在不同泛型類型中的類型。 這是您的操作方式:

從對象中提取屬性的類型 (Extract a property’s type from an object)

Let’s test it:

讓我們測試一下:

Extract inner types from function types

從函數(shù)類型中提取內(nèi)部類型

Let’s test it:

讓我們測試一下:

Extract generic types from a class or an interface

從類或接口中提取泛型類型

Let’s test it:

讓我們測試一下:

Extract types from an array

從數(shù)組中提取類型

Let’s test it:

讓我們測試一下:

Extract types from a tuple

從元組中提取類型

Let’s test it:

讓我們測試一下:

We tried to infer the type of the rest of the tuple into a type B but it did not work as expected. It is because TypeScript lacks of a feature that would allow us to deconstruct a tuple into another one. There is an active proposal that tackles these issues and you can expect improved manipulation for tuples in the future. This is why Tail is constructed the way it is.

我們試圖將其余元組的類型推斷為B型,但未按預(yù)期工作。 這是因?yàn)門ypeScript 缺少一項(xiàng)功能,該功能使我們可以將一個(gè)元組解構(gòu)為另一個(gè)元組。 有一個(gè)積極的提案可以解決這些問題,您可以期望將來對元組的操作得到改進(jìn)。 這就是為什么以這種方式構(gòu)造Tail原因。

infer is very powerful and it will be your main tool for type manipulation.

infer功能非常強(qiáng)大,它將成為您進(jìn)行類型操作的主要工具

咖喱V0 (Curry V0)

The warm-up ? is over, and you have the knowledge to build a “classic curry”. But before we start, let’s summarize (again) what it must be able to do:

熱身? 結(jié)束了,您就可以制作“經(jīng)典咖喱”了。 但是在開始之前,讓我們總結(jié)一下(再次)它必須具備的功能:

Our first curry type must take a tuple of parameters P and a return type R. It is a recursive function type that varies with the length of P:

我們的第一個(gè)咖喱類型必須采用參數(shù) P的元組和返回類型R 這是一種遞歸函數(shù)類型, P的長度 而變化

If HasTail reports false, it means that all the parameters were consumed and that it is time to return the return type R from the original function. Otherwise, there’s parameters left to consume, and we recurse within our type. Recurse? Yes, CurryV0 describes a function that has a return type of CurryV0 as long as there is a Tail (HasTail<P> extends true).

如果HasTail報(bào)告false ,它意味著所有的參數(shù)進(jìn)行了消費(fèi) ,現(xiàn)在是時(shí)候回到返回類型R從原來的功能。 否則, 剩下的參數(shù)要消耗掉 ,我們在類型中遞歸 。 遞歸? 是的, CurryV0描述了一個(gè)函數(shù),該函數(shù)的返回類型為CurryV0 ,只要有一個(gè)Tail ( HasTail<P> extend s true)即可??。

This is as simple as it is. Here is the proof, without any implementation:

這很簡單。 這是證明,沒有任何實(shí)現(xiàn):

But let’s rather visualize the recursion that happened above, step by step:

但是,讓我們逐步地可視化上面發(fā)生的遞歸:

And of course, type hints work for an unlimited amount of parameters ?:

當(dāng)然,類型提示可用于無限數(shù)量的參數(shù)?:

咖喱V1 (Curry V1)

Nice, but we forgot to handle the scenario where we pass a rest parameter:

很好,但是我們忘記處理傳遞rest參數(shù)的情況

We tried to use a rest parameter, but it won’t work because we actually expected a single parameter/argument that we earlier called arg0. So we want to take at least one argument arg0 and we want to receive any extra (optional) arguments inside a rest parameter called rest. Let’s enable taking rest parameters by upgrading it with Tail & Partial:

我們嘗試使用rest參數(shù),但是它無法正常工作,因?yàn)槲覀儗?shí)際上期望我們之前稱為arg0單個(gè)參數(shù)/參數(shù)。 因此,我們希望至少接受一個(gè)參數(shù)arg0 ,并且希望在稱為rest的rest參數(shù)內(nèi)接收任何其他(可選)參數(shù)。 讓我們通過使用Tail & Partial進(jìn)行升級來獲取剩余參數(shù):

Let’s test it:

讓我們測試一下:

But we made a horrible mistake: the arguments are consumed very badly. According to what we wrote, this will not produce a single TS error:

但是我們犯了一個(gè)可怕的錯(cuò)誤:爭論被非常嚴(yán)重地消耗了。 根據(jù)我們寫的內(nèi)容,這不會產(chǎn)生單個(gè)TS錯(cuò)誤:

In fact there is a big design problem because we said that we would force taking a single arg0. Somehow, we are going to need to keep track of the arguments that are consumed at a time. So, we will first get rid of arg0 and start tracking consumed parameters:

實(shí)際上存在一個(gè)很大的設(shè)計(jì)問題,因?yàn)槲覀冋f過將強(qiáng)制采用單個(gè)arg0 。 不知怎的,我們將需要持續(xù)跟蹤在一段時(shí)間使用的參數(shù)。 因此,我們將首先擺脫arg0并開始跟蹤消耗的參數(shù):

There, we made use of a constrained generic called T that is going to track any taken arguments. But now, it is completely broken, there is no more type checks because we said that we wanted to track any[] kind of parameters (the constraint). But not only that, Tail is completely useless because it only worked well when we took one argument at a time.

在那里,我們使用了一個(gè)受約束的泛型T ,該泛型將跟蹤任何采用的參數(shù)。 但是現(xiàn)在,它已經(jīng)完全崩潰了,不再需要類型檢查了,因?yàn)槲覀冋f過我們想跟蹤any[]類型的參數(shù)(約束)。 不僅如此, Tail完全沒有用,因?yàn)橹挥挟?dāng)我們一次提出一個(gè)論點(diǎn)時(shí), Tail才能很好地工作。

There is only one solution: some more tools ?.

只有一種解決方案: 更多工具

遞歸類型 (Recursive types)

The following tools are going to be used to determine the next parameters to be consumed. How? By tracking the consumed parameters with T we should be able to guess what’s left.

以下工具將用于確定下一個(gè)要使用的參數(shù)。 怎么樣? 通過使用T跟蹤消耗的參數(shù),我們應(yīng)該能夠猜測還剩下什么

Fasten your seat belt! You are about to learn another powerful technique ?:

系好安全帶! 您將要學(xué)習(xí)另一種強(qiáng)大的技術(shù)嗎?:

持續(xù) (Last)

Take your time to try to understand this complex yet very short type. Thisexample takes a tuple as a parameter and it extracts its last entry out:

花點(diǎn)時(shí)間嘗試?yán)斫膺@種復(fù)雜但很短的類型。 該示例將一個(gè)元組作為參數(shù),并提取出最后一個(gè)條目:

Let’s test it:

讓我們測試一下:

This example demonstrates the power of conditional types when used as an indexed type’s accessor. A what? A conditional type that accesses a type’s inner types in a command line fashion. For a more visual explanation:

此示例演示了用作索引類型的訪問器時(shí)條件類型的強(qiáng)大功能。 什么啊 以命令行方式訪問類型的內(nèi)部類型的條件類型。 有關(guān)更直觀的說明:

This technique is an ideal approach and a safe way to do recursion like we just did. But it is not only limited to recursion, it is a nice and a visual way to organise complex conditional types.

這種技術(shù)是一種理想的方法,并且像我們剛才一樣是進(jìn)行遞歸的安全方法。 但這不僅限于遞歸,它還是一種組織復(fù)雜條件類型的好方法

基本工具1 (Basic tools 1)

Where were we? We said that we needed tools in order to track arguments. It means that we need to know what parameter types we can take, which ones have been consumed and which ones are the next to come. Let’s get started:

我們剛剛說到哪了? 我們說我們需要工具來跟蹤論據(jù) 。 這意味著我們需要知道可以采用的參數(shù)類型,已消耗的參數(shù)類型以及接下來要使用的參數(shù)類型。 讓我們開始吧:

長度 (Length)

To do the analysis mentioned above, we will need to iterate over tuples. Asof TypeScript 3.4.x, there is no such iteration protocol that could allow us to iterate freely (like a for). Mapped types can map from a type to another, but they are too limiting for what we want to do. So, ideally, we would like to be able to manipulate some sort of counter:

為了進(jìn)行上述分析,我們將需要遍歷元組。 從TypeScript 3.4.x開始,沒有這樣的迭代協(xié)議可以讓我們自由地進(jìn)行迭代(如for )。 映射的類型可以從一個(gè)類型映射到另一個(gè)類型,但是對于我們要執(zhí)行的操作來說,它們太局限了。 因此,理想情況下,我們希望能夠操縱某種計(jì)數(shù)器

Let’s test it:

讓我們測試一下:

By topping a tuple up with any, we created something that could be similar to a variable that can be incremented. However, Length is just about giving the size of a tuple, so it also works with any other kind of tuple:

通過用any 填充一個(gè)元組,我們創(chuàng)建了一個(gè)類似于可以遞增的變量的東西。 但是, Length只是給出一個(gè)元組的大小,因此它也可以與任何其他類型的元組一起使用:

前置 (Prepend)

It adds a type E at the top of a tuple T by using our first TS trick:

通過使用我們的第一個(gè)TS技巧,它在元組T的頂部添加類型E :

Let’s test it:

讓我們測試一下:

In Length’s examples, we manually increased a counter. So Prepend is the ideal candidate to be the base of a counter. Let’s see how it would work:

在Length的示例中,我們手動增加了一個(gè)計(jì)數(shù)器。 因此Prepend是成為計(jì)數(shù)器基礎(chǔ)的理想人選。 讓我們看看它如何工作:

下降 (Drop)

It takes a tuple T and drops the first N entries. To do so we are going to use the same techniques we used in Last and our brand new counter type:

它取一個(gè)元組T并刪除前N個(gè)條目。 為此,我們將使用與Last和全新計(jì)數(shù)器類型相同的技術(shù):

Let’s test it:

讓我們測試一下:

What happened?

發(fā)生了什么?

The Drop type will recurse until Length<;I> matches the value of N that we passed. In other words, the type of index 0 is chosen by the conditional accessor until that condition is met. And we used Prepend&lt;any, I> so that we can increase a counter like we would do in a loop. Thus, Length<I> is used as a recursion counter, and it is a way to freely iterate with TS.

Drop類型將遞歸直到Length< ; I>米 atches瓦爾u的N個(gè)E,我們通過。 換言之,類型i ndex 0由條件存取選擇,直到滿足條件。 我們used Prepend&l噸;任何,我>這樣我們就可以增加一個(gè)計(jì)數(shù)器一樣,我們會在做loop. Thu loop. Thu ,將Length <I> 用作遞歸計(jì)數(shù)器,這是一種自由迭代TS的方法。

咖喱V3 (Curry V3)

It’s been a long and tough road to get here, well done! There’s a reward for you ?.

到達(dá)這里,路途艱難,艱難! 有獎(jiǎng)勵(lì)給你嗎?

Now, let’s say that we tracked that 2 parameters were consumed by our curry:

現(xiàn)在,假設(shè)我們跟蹤了咖喱消耗了兩個(gè)參數(shù):

Because we know the amount of consumed parameters, we can guess the ones that are still left to be consumed. Thanks to the help of Drop, we can do this:

因?yàn)槲覀冎老牡膮?shù)數(shù)量,所以我們可以猜測仍然需要消耗的參數(shù)。 感謝Drop的幫助,我們可以做到這一點(diǎn):

It looks like Length and Drop are precious tools. So let’s revamp our previous version of curry, the one that had a broken Tail:

看起來Length和Drop是寶貴的工具。 因此,讓我們修改一下先前版本的咖喱,咖喱的Tail壞了:

What did we do here?

我們在這里做了什么?

First, Drop<Length<T>, P> means that we remove consumed parameters out.Then, if the length of Drop&lt;Length<T>, P> is not equal to 0, our curry type has to continue recursing with the dropped parameters until… Finally, when all of the parameters were consumed, the Length of the dropped parameters is equal to 0, and the return type is R.

首先, Drop<Length< T>,P>表示我們刪除消耗的參數(shù),然后,如果e length of Drop&l t; Length <T> , P>的長度不等于0 ,則我們的咖喱類型h繼續(xù)遞歸與下降paramete RS直到......最后,當(dāng)所有 parame ters w ERE消耗的長度下降 參數(shù)等于0, 返回類型為R。

咖喱V4 (Curry V4)

But we’ve got another error above: TS complains that our Drop is not of type any[]. Sometimes, TS will complain that a type is not the one you expected, but you know it is! So let’s add another tool to the collection:

但是上面有另一個(gè)錯(cuò)誤:TS抱怨我們的Drop不是type any[]類型。 有時(shí),TS會抱怨一種類型不是您期望的那種,但您知道它是! 因此,我們向集合添加另一個(gè)工具:

(Cast)

It requires TS to re-check a type X against a type Y, and type Y will only be enforced if it fails. This way, we’re able to stop TS’s complaints:

它要求TS根據(jù)類型Y 重新檢查類型X ,并且類型Y僅在失敗時(shí)才被強(qiáng)制執(zhí)行。 這樣,我們就可以停止TS的投訴:

Let’s test it:

讓我們測試一下:

And this is our previous curry, but without any complaint this time:

這是我們以前的咖喱,但是這次沒有任何抱怨:

Remember earlier, when we lost the type checks because we started tracking consumed parameters with T extends any[]? Well it has been fixed by casting T to Partial<;P>. We added a constraint withCast<T,Partial<P>>!

還記得以前,當(dāng)我們因?yàn)殚_始使用T extends any[]跟蹤消耗的參數(shù)而丟失類型檢查時(shí), T extends any[]嗎? 好吧,已通過將T強(qiáng)制轉(zhuǎn)換為Partial< ; P>進(jìn)行了修復(fù)。 我們用t withCast<T,Pa Partial <P >>添加了約束t withCast<T,Pa !

Let’s test it:

讓我們測試一下:

咖喱V5 (Curry V5)

Maybe you thought that we were able to take rest parameters. Well, I am very sorry to inform you that we are not there yet. This is the reason why:

也許您以為我們可以接受其他參數(shù)。 好吧,很抱歉通知您我們還沒有到。 這就是為什么:

Because rest parameters can be unlimited, TS’s best guess is that the length of our tuple is a number, it’s kind of clever! So, we cannot make use of Length while dealing with rest parameters. Don’t be sad, it’s not so bad:

因?yàn)閞est參數(shù)可以是無限的 ,所以TS最好的猜測是我們的元組的長度是一個(gè)number ,這很聰明! 因此,在處理其余參數(shù)時(shí),我們不能使用Length 。 別難過,還不錯(cuò):

When all the non-rest parameters are consumed, Drop<Length<;T>,P> can only match […any[]]. Thanks to this, we used [any,…any[] as a condition to end the recursion.

當(dāng)所有非休息參數(shù)都被消耗時(shí), Drop<Length< ; T>,P> only ma匹配[…any []]。 由于日is, we used [任何,任何... []為AC OND銀行足球比賽結(jié)束遞歸。

Let’s test it:

讓我們測試一下:

Everything works like a charm ?. You just got yourself a smart, generic, variadic curry type. You will be able play with it very soon… But before you do so, what if I told you that our type can get even more awesome?

一切都像魅力嗎? 您只是得到了一種聰明, 充滿活力, 雜色的咖喱類型。 您很快就能使用它了……但是在您這樣做之前,如果我告訴您我們的類型可以變得更出色,該怎么辦?

占位符 (Placeholders)

How awesome? We are going give our type the ability to understand partial application of any combination of arguments, on any position. According to Ramda’s documentation, we can do so by using a placeholder called _. It states that for any curried function f, these calls are equivalent:

太棒了 我們將使我們的類型能夠理解在任意位置上參數(shù)任意組合的部分應(yīng)用。 根據(jù)Ramda的文檔,我們可以使用名為_的占位符來實(shí)現(xiàn)。 它指出,對于任何咖喱函數(shù)f ,這些調(diào)用是等效的:

A placeholder or “gap” is an object that abstracts the fact that we are notcapable or willing to provide an argument at a certain moment. Let’s start bydefining what a placeholder is. We can directly grab the one from Ramda:

占位符或“空白”是抽象化以下事實(shí)的對象:我們沒有能力或不愿意在某一時(shí)刻提供論據(jù)。 讓我們先定義一個(gè)占位符。 我們可以直接從Ramda那里搶一個(gè):

Earlier, we have learnt how to do our first type iterations by increasing a tuple’s length. In fact, it is a bit confusing to use Length and Prepend on our counter type. And to make it clearer, we will refer to that counter as an iterator from now on. Here’s some new aliases just for this purpose:

之前,我們學(xué)習(xí)了如何通過增加元組的長度來進(jìn)行第一類迭代。 實(shí)際上,在我們的計(jì)數(shù)器類型上使用Length和Prepend有點(diǎn)令人困惑。 為了更清楚一點(diǎn) ,我們將從現(xiàn)在開始將該計(jì)數(shù)器稱為迭代器 。 這是一些新的別名,專門用于此目的:

位置(位置) (Pos (Position))

Use it to query the position of an iterator:

使用它查詢迭代器的位置:

下一個(gè)(+1) (Next (+1))

It brings the position of an iterator up:

它提高了迭代器的位置:

上一頁(-1) (Prev (-1))

It brings the position of an iterator down:

它降低了迭代器的位置:

Let’s test them:

讓我們測試一下:

迭代器 (Iterator)

It creates an iterator (our counter type) at a position defined by Index and is able to start off from another iterator’s position by using From:

它在Index定義的位置創(chuàng)建一個(gè)迭代器(我們的計(jì)數(shù)器類型),并能夠使用From從另一個(gè)迭代器的位置開始:

Let’s test it:

讓我們測試一下:

基本工具2 (Basic tools 2)

Good, so what do we do next? We need to analyze whenever a placeholder is passed as an argument. From there, we will be able to tell if a parameter has been “skipped” or “postponed”. Here’s some more tools for this purpose:

好,接下來我們該怎么做? 每當(dāng)占位符作為參數(shù)傳遞時(shí),我們都需要進(jìn)行分析 。 從那里,我們將能夠判斷參數(shù)是否已“跳過”或“推遲”。 以下是一些用于此目的的工具:

逆轉(zhuǎn) (Reverse)

Believe it or not, we still lack a few basic tools. Reverse is going to give us the freedom that we need. It takes a tuple T and turns it the other way around into a tuple R, thanks to our brand new iteration types:

信不信由你,我們?nèi)匀蝗鄙僖恍┗竟ぞ摺?Reverse將給我們我們所需要的自由。 由于我們?nèi)碌牡愋?#xff0c;它需要一個(gè)元組T并將其反過來變成一個(gè)元組R :

Let’s test it:

讓我們測試一下:

康卡特 (Concat)

And from Reverse, Concat was born. It simply takes a tuple T1 and merges it with another tuple T2. It’s kind of what we did in test59:

Concat )從Reverse出生。 它只需要一個(gè)元組T1并將其與另一個(gè)元組T2合并。 這是我們在test59所做的test59 :

Let’s test it:

讓我們測試一下:

附加 (Append)

Enabled by Concat, Append can add a type E at the end of a tuple T:

由Concat啟用, Append可以在元組T的末尾添加E類型:

Let’s test it:

讓我們測試一下:

缺口分析 (Gap analysis)

We now have enough tools to perform complex type checks. But it’s been a while since we discussed this “gap” feature, how does it work again? When a gap is specified as an argument, its matching parameter is carried over to the next step (to be taken). So let’s define types that understand gaps:

現(xiàn)在,我們有足夠的工具來執(zhí)行復(fù)雜的類型檢查 。 但是自從我們討論此“差距”功能以來已經(jīng)有一段時(shí)間了,它又如何工作? 如果將間隙指定為自變量,則其匹配參數(shù)將繼續(xù)進(jìn)行下一步(要執(zhí)行)。 因此,讓我們定義理解差距的類型:

差距 (GapOf)

It checks for a placeholder in a tuple T1 at the position described by an iterator I. If it is found, the matching type is collected at the same position in T2 and carried over (saved) for the next step through TN:

它在迭代器I所描述的位置檢查元組T1中的占位符。 如果找到,則在T2的相同位置收集匹配類型,并通過TN保留(保存)用于下一步:

Let’s test it:

讓我們測試一下:

差距 (GapsOf)

Don’t be impressed by this one. It calls Gap over T1 & T2 and stores the results in TN. And when it’s done, it concats the results from TN to the parameter types that are left to be taken (for the next function call):

不要對此印象深刻。 它在T1和T2調(diào)用Gap ,并將結(jié)果存儲在TN 。 而當(dāng)它完成,它c(diǎn)oncats結(jié)果從TN到留采取(下一個(gè)函數(shù)調(diào)用)的參數(shù)類型:

Let’s test it:

讓我們測試一下:

縫隙 (Gaps)

This last piece of the puzzle is to be applied to the tracked parameters T. We will make use of mapped types to explain that is is possible replace any argument with a placeholder:

難題的最后一部分將應(yīng)用于跟蹤的參數(shù)T 我們將使用映射類型來解釋是否有可能用占位符替換任何參數(shù):

A mapped type allows one to iterate and alter properties of another type. In this case, we altered T so that each entry can be of the placeholder type. And thanks to ?, we explained that each entry of T is optional. It means that we no longer have the need to use Partial on the tracked parameters.

映射類型允許一個(gè)對象迭代和更改另一種類型的屬性 。 在這種情況下,我們更改了T以便每個(gè)條目都可以是占位符類型。 并感謝? ,我們解釋說T每個(gè)條目都是可選的。 這意味著我們不再需要對跟蹤的參數(shù)使用Partial 。

Let’s test it:

讓我們測試一下:

Ugh, we never said that we could take undefined! We just wanted to be able to omit a part of T. It is a side effect of using the ? operator. But it is not that bad, we can fix this by re-mapping with NonNullable:

gh,我們從未說過我們可以采取undefined ! 我們只是希望能夠省略T的一部分。 使用? 副作用 操作員。 但這還不錯(cuò),我們可以通過使用NonNullable重新映射來解決此NonNullable :

So let’s put the two together and get what we wanted:

因此,讓我們將兩者放在一起,得到我們想要的東西:

Let’s test it:

讓我們測試一下:

咖喱V6 (Curry V6)

We’ve built the last tools we will ever need for our curry type. It is now time to put the last pieces together. Just to remind you, Gaps is our new replacement for Partial, and GapsOf will replace our previous Drop:

我們已經(jīng)建立了咖喱類型所需的最后一種工具。 現(xiàn)在是時(shí)候?qū)⒆詈蟮牟糠址旁谝黄鹆恕?提醒您, Gaps是Partial的新替代品, GapsOf將替代我們之前的Drop :

Let’s test it:

讓我們測試一下:

In order to make sure that everything works as intended, I am going to force the values that are to be taken by the curried example function:

為了確保一切正常,我將強(qiáng)制使用咖喱的示例函數(shù)采用的值:

There is just a little problem: it seems like we’re a bit ahead of Ramda! Our type can understand very complex placeholder usages. In other words, Ramda’s placeholders just don’t work when they’re combined with rest parameters ?:

只是有一個(gè)小問題:似乎我們比Ramda領(lǐng)先! 我們的類型可以理解非常復(fù)雜的占位符用法。 換句話說,當(dāng)Ramda的占位符與其余參數(shù)組合在一起時(shí),它們的占位符不起作用 ?:

However, even if this looks perfectly correct, it will result in a complete crash. This happens because the implementation of Ramda’s curry does not deal well with combinations of placeholders and rest parameters. This is why I opened a ticket with Ramda on Github, in the hope that the types we’ve just created could one day work in harmony with the library.

但是,即使看起來完全正確,也將導(dǎo)致完全崩潰。 發(fā)生這種情況是因?yàn)镽amda的curry的實(shí)現(xiàn)不能很好地處理占位符和rest參數(shù)的組合。 這就是為什么我在Github上與Ramda一起開票的原因,希望我們剛剛創(chuàng)建的類型有一天可以與圖書館和諧地工作。

咖喱 (Curry)

This is very cute, but we have one last problem to solve: parameter hints. I don’t know about you, but I use parameter hints a lot. It is very useful to know the names of the parameters that you’re dealing with. The version above does not allow for these kind of hints. Here is the fix:

這很可愛,但是我們要解決的最后一個(gè)問題是: 參數(shù)提示 。 我不了解您,但是我經(jīng)常使用參數(shù)提示。 知道您要處理的參數(shù)的名稱非常有用。 上面的版本不允許出現(xiàn)此類提示。 解決方法是:

I admit, it’s completely awful! However, we got hints for Visual Studio Code.What did we do here? We just replaced the parameter types P & R that used to stand for parameter types and return type, respectively. And instead, we used the function type F from which we extracted the equivalent of P with Parameters<;F>; and R with ReturnType<F>. Thus, TypeScript is able to conserve the name of the parameters, even after currying:

我承認(rèn),這簡直太糟糕了! 但是,我們得到了有關(guān)Visual Studio Code的提示。我們在這里做了什么? 我們只是替換了分別代表參數(shù)類型和返回類型的參數(shù)類型P和R 取而代之的是,我們使用函數(shù)類型 F ,從中提取與Parameters< ; F> ;等效的P 和R with ReturnT ype <F>。 因此,TypeScript能夠保留參數(shù)名稱,即使是在經(jīng)過以下操作之后也是如此:

There’s just one thing: when using gaps, we’ll lose the name of a parameter.

只有一件事:使用間隙時(shí),我們將丟失參數(shù)的名稱。

A word for IntelliJ users only: You won’t be able to benefit from proper hints. I recommend that you switch to Visual Studio Code as soon as possible. And it is community-driven, free, much (much) faster, and supports key bindings for IntelliJ users. :)

僅適用于IntelliJ用戶:您將無法從適當(dāng)?shù)奶崾局惺芤妗?我建議您盡快切換到Visual Studio Code。 它是社區(qū)驅(qū)動的,免費(fèi)的,快得多(很多),并且支持IntelliJ用戶的按鍵綁定。 :)

最后的話 (LAST WORDS)

Finally, I would like to inform you that there is a current proposal for variadic types. What you’ve learned here is not going to become obsolete — this proposal aims to ease the most common tuple type manipulations, so it is a very good thing for us. In a close future, it will enable easier tuple concatenations like the Append, Concat, and Prepend we’ve built, as well as destructuring and a better way to describe variable function parameters.

最后,我想通知您,目前針對可變參數(shù)類型的建議。 您在這里學(xué)到的內(nèi)容不會過時(shí)-此建議旨在簡化最常見的元組類型的操作,因此對我們來說是一件非常好的事。 在不久的將來,它將使我們構(gòu)建的Append , Concat和Prepend類的元組連接更加容易,并進(jìn)行結(jié)構(gòu)Prepend和更好的描述可變函數(shù)參數(shù)的方法。

That’s it. I know that it’s a lot to digest at once, so that’s why I released a developer version of this article. You can clone it, test it, and change it with TypeScript 3.3.x and above. Keep it close to you and learn from it until you become more comfortable with the different techniques ?.

而已。 我知道一次要消化很多東西,所以這就是為什么我發(fā)布了本文的開發(fā)人員版本 。 您可以克隆,測試它,并使用TypeScript 3.3.x及更高版本對其進(jìn)行更改。 讓它靠近您并從中學(xué)習(xí),直到您對各種技術(shù)變得更熟悉為止。

High-five ? if you enjoyed this guide, and stay tuned for my next article!

舉手擊掌 ? 如果您喜歡本指南,請繼續(xù)關(guān)注我的下一篇文章!

EDIT: It’s available for Ramda 0.26.1

編輯: 它可用于Ramda 0.26.1

Thanks for reading. And if you have any questions or remarks, you are morethan welcome to leave a comment.

感謝您的閱讀 。 如果您有任何疑問或評論,歡迎您發(fā)表評論。

翻譯自: https://www.freecodecamp.org/news/typescript-curry-ramda-types-f747e99744ab/

typescript

總結(jié)

以上是生活随笔為你收集整理的typescript_如何掌握高级TypeScript模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。