Dotevo programowanie [#1] - TypeScript

in #polish3 years ago (edited)

Postanowiłem napisać pewną serię związaną z moim życiem zawodowym. Pozwoli mi to na podzielenie się z Wam pewnymi doświadczeniami, a przy okazji zmotywuje do napisania czegoś na Hive.

Wstęp

Jestem zawodowo programistą od 9 lat i przez ten czas pracowałem w różnych technologiach i pisałem bardzo różne rzeczy - od urządzeń wbudowanych, automotive i technologii mobilnych po technologie webowe, z którymi pracuję aktualnie. Przez te lata zauważyłem, że podejście w zależności od obszaru jest zupełnie inne, ale też takie szerokie znajomości pomagają w pracy.

TypeScript

typescriptlogoB29A3F462Dseeklogo.com_.webp

Pisałem w wielu językach programowania. Zaczynałem od C i C++ w podstawówce, potem w technikum próbowałem sił również w perlu. Na studiach była Java, ale kilka projektów zrobiłem w C++. Pisałem też wtedy swoje pierwsze projekty w JavaScript. W pracy doszedł potem Python, a po drodze liznąłem jeszcze kilku języków. Lecz jedno jest pewne. Zawsze w pythonie brakowało mi typowania z javy i C++, a w C++ brakowało mi dynamicznych template (takich na poziomie runtime) z Javy. W Javie denerwowała zaś mnie wydajność. Każdy język ma swoje wady i zalety. Największą wadą i zaletą JS w mojej opinii były typy. Dlaczego wadą i zaletą? Bo czasem fajnie było napisać stronę w leaflet i rozszerzyć pewne obiekty używając prototype. Był to dla mnie taki wytrych, który pozwalał na bardzo dużo. Z drugiej strony, czasem zależało mi abym miał wygodne podpowiadanie propsów w obiekcie i aby one tam faktycznie były. TypeScript to pod tym względem najlepszy język bo pozwala zachować elastyczność i dodać do tej elastyczności pewne reguły.
Dodatkowo nawej najbardziej typowany język nie pozwala tak dobrze typować jak Typescript. Przykład:

type PowerStatus = 'on' | 'off';
const variable: PowerStatus = 'on';
const variable2: PowerStatus = 'string';   // Type '"string"' is not assignable to type 'PowerStatus'

Od roku jestem w projekcie, który używa Nest.js i TypeScript oraz kilku innych fajnych technologii, o których może napiszę kiedyś indziej. Dzisiaj skupię się na TS, który nie tak dawno dorobił się wydania 4.1. Co dla mnie jest osobiście ważne, że to pierwsze wydanie, którego używałem zanim pojawiło się w wersji stabilnej, a zmiany, które przynosi były mi znane jeszcze przed latem.

Template Literal Types to w mojej opinii najlepszy feature, który się pojawił bo nigdy jeszcze nie spędziłem tyle czasu na zabawę typami. Daje on ogromne możliwości. Wyobraźcie sobie, że macie funkcję, która przerabia obiekt tak, że np.:

{
  propercja_1: true,
  propercja_tekstowa: string,
}

zostaje zamieniona na:

{
  propercja1: true,
  propercjaTekstowa: string,
}

sprawa jest oczywiście bardzo prosta, gdy znamy dokładny model i w return type możemy podać inny model. Co jednak jeśli ta funkcja ma być generyczna?

Możemy wtedy napisać typ, który podmieni nam jakis_tekst na jakisTekst w ten sposób:

type Rename<
  S extends string
> = S extends `${infer PRE}_${infer POST}`
  ? Rename<`${PRE}${Capitalize<POST>}`>
  : S;

A następnie użyć kolejnej nowej rzeczy w języku czyli mapowania kluczy (słowo kluczowe as):


type ReplaceProps<O extends object> = {
  [P in keyof O & string as Rename<P>]: O[P];
};
type Test = {
  jakis_typ: number;
  jakis_string: string;
}

const w: ReplaceProps<Test> ={ jakisTyp: 3, jakisString: 'a'};

const z: ReplaceProps<Test> ={ jakisTyp: 3, jakis_string: 'a'};

//    Type '{ jakisTyp: number; jakis_string: string; }' is not assignable to type 
// ReplaceProps<Test>'. Object literal may only specify known properties, but 'jakis_string'
// does not exist in type 'ReplaceProps<Test>'. Did you mean to write 'jakisString'?

Fajne prawda? Zrobiłem już tyle różnych własnych typów używając tego, że mam wrażenie, że za jakiś czas nie będę potrafił żyć bez tego. Możliwość podawania {TYP} na poziomie typów to prawdziwy As w rękawie.

Od pewnego czasu pracuję w weekendy nad własną nakładką na infrastrukturę. Coś podobnego do ORM, ale jedynie do odczytu, ale za to zawiera różne dodatkowe funkcje.
Podrzucę kilka rozwiązań:

export class ResultBuilder<T extends object> {
  public select(list: SelectorKeys<T> | SelectorKeys<T>[]) {}
  public filter<K extends keyof Filter<T>, L extends Filter<T>[K]>(
    list: K,
    ...args: Parameters<L>
  ) {}
}
//-------------------------------------------------------------------
type AEntity = {
  a: number;
  b: string; 
  differentMethod(active: boolean, end: number): void;
};
type BEntity = {
  aEntity: AEntity;
  testProperty: number; 
  isActive(active: boolean): void;
};

const rb = new ResultBuilder<BEntity>();
rb.select('testProperty');
rb.select('aEntity.a');
rb.select('aEntity.as'); // Failed because of missing property
rb.filter('isActive', true);
rb.filter('isActive', 42); // Failed because of wrong filter type
rb.filter('aEntity.differentMethod', true); // Failed because of only one param
rb.filter('aEntity.differentMethod', true, 42);

Pełny przykład

Jest jeszcze wiele rzeczy, które próbowałem pisać. Ale zostawię sobie te rzeczy na później ;-)

Miłego programowania!

Sort:  

Congratulations @dotevo! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s) :

You received more than 3750 upvotes. Your next target is to reach 4000 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @hivebuzz:

Feedback from the January 1st Hive Power Up Day
Happy New Year - Project Activity Update