TypeScript kodunuzda Option'ı fp-ts ile kullanma

Jan 30 2023
Seçenek, verileri iki olası durumla depolamanıza yardımcı olan Veri Yapısıdır (Kapsayıcı): değer yok (Yok) veya değer mevcut (Bazı). JavaScript'te bir Nesnede isteğe bağlı bir değeri temsil etmek için Option'ı kullanabiliriz.

SeçenekContainer , verileri iki olası durumla depolamanıza yardımcı olanVeri Yapısıdır ( ) : değer yok (None) veya değer mevcut (Some). JavaScript'tebir Nesnede isteğe bağlı bir değeri temsil etmek için Option'ı kullanabiliriz

Örneğin, bir Kişi Nesne Türümüz ve bir Adres Nesne Türümüz olduğunu varsayalım:

type Address = {
  addressLine1: string;
  addressLine2?: string;
};

type Person = {
  name: string;
  age: number;
  address?: Address;
};

const getAddressLine2 = (person: Person) => {
  // To get the address line 2,
  // Need to check whether the value is empty (null or undefined or "")
  if (person.address && person.address.addressLine2) {
     return person.address.addressLine2;
  }
  // Return an empty string as the default value;
  return "";
};

import * as option from 'fp-ts/Option';

const getAddressLine2 = (person: Person) => {
  /*
    Step 1: create an Option container and wrap the data inside Option becasue address can be empty
    Use the Option constructor "option.fromNullable"
  */
  const addressOption = option.fromNullable(person.address);
  // Extract the value of addressLine2 from addressOption ...
};

Adres Nesnesi içindeki addressLine2'ye erişmek için sezgisel olarak şöyle bir şey yapmak istiyoruz .addressOption.addressLine2

Ancak , adresin değeri Option içinde sarıldığı için bunu yapamadık , bu nedenle adresi Option'dan açmamız ve ardından addressLine2 değerine erişmemiz gerekiyor .

Ayrıca, değer boş olabileceğinden, addressLine2 de Option'a sarılmalıdır . Dolayısıyla, address öğesinin boş olmayan değerini elde edebiliyorsak , aynısını addressLine2 için options.fromNullable(address.addressLine2) kullanarak yapabiliriz .

Aşağıdaki kod parçacıkları, addressLine2 değerini elde etmek için adresin boş olmayan değerini elde etmeye yönelik iki yaklaşımı gösterir :

import * as option from 'fp-ts/Option';

const getAddressLine2 = (person: Person) => {
  /*
    Step 1: create an Option container and wrap the data inside Option becasue address can be empty
    Use the Option constructor "option.fromNullable"
  */
  const addressOption = option.fromNullable(person.address);

  /*
    Step 2: Extract the value of addressLine2 from addressOption 
    Approach 1 -
    Use "option.isSome" to extract the value
  */
  if (option.isSome(addressOption)) {
    // The address value can be obtained by .value
    const address = addressOption.value;
    
    // Wrap the addressLine2 with Option
    const addressLine2Option = option.fromNullable(address.addressLine2);
  }
  // Return the value of addressLine2 ...
};
import * as option from 'fp-ts/Option';

const getAddressLine2 = (person: Person) => {
  /*
    Step 1: create an Option container and wrap the data inside Option becasue address can be empty
    Use the Option constructor "option.fromNullable"
  */
  const addressOption = option.fromNullable(person.address);
  /*
    Step 2: Extract the value of addressLine2 from addressOption
    Approach 2 - 
    Use "option.map" to extract the value and wrap the addressLine2 with Option
  */
 const addressLine2Option = option.map((address) => option.fromNullable(address.addressLine2))(addressOption);

 // Return the value of addressLine2 ...
};

.map'i kısaca açıklamak gerekirse , kapsayıcı içindeki değeri açmanıza ve verilerle bazı dönüşümler yapmanıza yardımcı olur. ( JS'de A rray ile çalışırken .map işlevine aşina görünüyor , değil mi? :))

Bekleyin… addressLine2Option türünü kontrol ederseniz , bu Option < Option < string >> şeklindedir , addressLine2 değeri bir Option ile başka bir Option içinde sarılır ! Ama bizim istediğimiz sadece< Option < string >>!

Option'daki " map " öğesinin tip imzasına ( tip imzası İşlevsel Programlamada ÇOK ÖNEMLİ ) bakarsanız , bu f: (a: A -> b: B) -> Option<a: A> -> Option< b:B>

Şu şekilde çalışır: Önce Option<a: A>' dan “a” verisini açar , “a” değerini f'ye girdi olarak verir , “a”yı “b”ye dönüştürür, ardından “b” sonucunu Seçenek ve iade et. (Not: Option<a: A> Yok ise, f işlevi tetiklenmez ve orijin değeri (Yok) döndürülür.)

Bu nedenle , daha önce < Option < string >> yerine Option < Option < string >> alırız !

Neyse ki, kolayca düzeltebiliriz! Her iki katman da Seçenek olduğundan, dış katmanı kaldırmak için .flatten kullanabiliriz :

const addressLine2Option = option.map((address) => option.fromNullable(address.addressLine2))(addressOption);
 const addressLine2OptionForReal = option.flatten(addressLine2Option);

const addressLine2Option = option.chain((address) => option.fromNullable(address.addressLine2))(addressOption);

Son olarak, eğer değer boşsa, addressLine2'nin değerini default değeri olan “” ile options.getOrElse kullanarak döndürmeliyiz :

import * as option from 'fp-ts/Option';

const getAddressLine2 = (person: Person) => {
  /*
    Step 1: create an Option container and wrap the data inside Option becasue address can be empty
    Use the Option constructor "option.fromNullable"
  */
  const addressOption = option.fromNullable(person.address);
  /*
    Step 2: Extract the value of addressLine2 from addressOption
    Approach 2 - 
    Use "option.chain" to extract the value and wrap the addressLine2 with Option and then flatten the result
  */
 const addressLine2Option = option.chain((address: Address) => option.fromNullable(address.addressLine2))(addressOption);
  /*
    Last Step: Return the value of addressLine2 with the default value "" if empty
  */
  const result = option.getOrElse(() => "")(addressLine2Option);
  return result;
};

const personA = {
  name: "A",
  age: 20,
  address: {
    addressLine1: "hehe",
    addressLine2: "haha",
  }
}

// You should get "haha"
getAddressLine2(personA);

Bilginize, verileri düzene sokmak için boru ve işlev bileşimi için akış kullanırdım (Güven bana, bu son sürüm!).

import * as option from 'fp-ts/Option';
import {pipe, flow} from 'fp-ts/function';

type Address = {
  addressLine1: string;
  addressLine2?: string;
};

type Person = {
  name: string;
  age: number;
  address?: Address;
};

const getAddressLine2Final = flow(
  option.fromNullableK((person: Person) => person.address),
  option.chain(address => option.fromNullable(address.addressLine2)),
  option.getOrElse(() => '')
);

const personA = {
  name: 'A',
  age: 20,
  address: {
    addressLine1: 'hehe',
    addressLine2: 'haha',
  },
};

// You should get "haha"
pipe(personA, getAddressLine2Final);

Nesnede daha iç içe geçmiş isteğe bağlı özellikler olduğunu hayal edin. Örnekler için, addressLine2 türü artık bir dize değildir, ancak şu şekilde değiştirilir:

type AddressLine2 = {
  country?: {
    city?: string;
  }
}

person.address && person.address.addressLine2 && 
person.address.addressLine2 && person.address.addressLine2.country && 
person.address.addressLine2.country.city

option.chain(addressLine2 => option.fromNullable(address.country)),
option.chain(country => option.fromNullable(country.city)),

fp-ts hakkında daha fazla bilgi .

Okuduğunuz için teşekkürler
Umarım bu makaleyi beğenir ve ondan bir şeyler kazanırsınız. Fonksiyonel Programlamayı öğrenmenin artık sizin için daha az göz korkutucu olmasını diliyorum :)

Ka Hin