Typescript w Sealiousie!

Po ostatnich dużych refactorach Sealiousa zauważyłem, że bardzo często zostawiam błędy w postaci “ta funkcja przyjmuje o jeden argument mniej, a go nie usunąłeś”, lub “podałem argumenty w złej kolejności”.

Sprowokowało mnie to do zastanowienia się, czy nie byłoby fajnie zacząć korzystać z TypeScripta. Dokonuje on statycznej analizy kodu i wychwytuje takie rzeczy, ale nie narzuca się za bardzo i czyni typowanie opcjonalnym.

Jestem ciekaw Waszych opinii :slight_smile:

1 Like

Tak jak wspomniałem - ta myśl już zagościła w mojej głowie i uważam, że to dobry pomysł. Zrobię mały research w temacie TypeScript vs Flow w wolnej chwili i podziele się :slight_smile:

1 Like

Używam w Angularze aka Angulaszu 5 i bardzo sobie chwalę :slight_smile: stack tslint + prettier współgrają do tego doskonale.

2 Likes

Zastanawiam się, jak trzeba sobie poradzić z TypeScriptem na backendzie - bo trzeba użyć preprocessora wtedy, prawda? :thinking:

Bez znaczenia gdzie to zostanie użyte potrzebny jest kompilator który skompliuje pliki TypeScript do regularnego JavaScriptu. Np. w @angular-cli jest watcher z webpacka który rekompiluje wszystkie zmiany w locie i działa to bardzo płynnie.

Primo, pytanie na ile można w nim korzystać z ficzerów ES6/7.
Secundo, pytanie czy to “nie narzuca się za bardzo” nie okaże się jednak za bardzo.

Chyba ja bym się wstrzymał z dodaniem tego do Sealiousa i zrealizował na tym jakiś inny, mniejszy projekt - to dałoby nam dobry ogląd sytuacji przy niewielkim koszcie jeżeli TS okazałby się jednak zbyt wkurzający.

1 Like

Myślę, że warto spróbować najpierw gdzieś indziej. Jestem też ciekaw, jakie wnioski przyniesie risercz Bartka :wink:

Póki co i tak musimy sobie radzić assertami :stuck_out_tongue:

@piotr-ptaszynski możesz korzystać z ES6/7 - wystarczą odpowiednie transpilery

1 Like

@kuba-orlik mój błąd odnośnie kompilatora, nie jest potrzebny np. https://github.com/TypeStrong/ts-node

hmm tylko wtedy wymagamy, aby cała aplikacja była odpalana za pomocą ts-node, prawda? Trochę wirusowe mi się to wydaje, chociaż i tak odpalamy apki w dockerowo izolowanym środowisku, więc who cares :stuck_out_tongue:

Warte przetestowania, zawsze można kompilować - bo produkcyjnie i tak jsowy plik wynikowy będzie szybszy (chyba?). :thinking:

Jeden z krótkich przykładów dlaczego podoba mi się TypeScript, mam sobie taki komponent i metodę add:

// heroes.component.ts

import { Hero } from "../hero";
import { HeroService } from "../hero.service";

export class HeroesComponent{
	constructor(private heroService: HeroService) {}

	add(name: string): void {
		const newHero = new Hero();
		newHero.name = name;
		this.heroService.addHero(newHero).subscribe();
	}

// Parametr dla `this.heroService.addHero` można zapisać krócej po prostu ` { name } `
// albo dodatkowo z użyciem słowa kluczowego `as` co znaczy mniej więcej tyle że
// mówimy do type checkera traktuj to coś `{ name }` jako obiekt innego typu - typu `Hero`

	add2(name: string): void {
		this.heroService.addHero({ name } as Hero).subscribe();
	}

}

Automatycznie dostaniemy błąd jeżeli podamy obiekt innego typu ponieważ w serwisie ta metoda ma przyjąć zmienną typu Hero:

// hero.service.ts
addHero(hero: Hero): Observable<Hero> {
	return this.http
		.post<Hero>(this.heroesUrl, hero)
		.pipe(
			tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
			catchError(this.handleError<Hero>("addHero"))
		);
}

Oczywiście moglibyśmy zainicjonalizować obiekt Hero bezpośrednio jako argument do metody addHero komponentu heroesComponent przez new Hero(name), ale definicja modelu Hero wygląda następująco (nie mamy konstruktora):

// hero.ts
export class Hero {
	id: number;
	name: string;
}

Syntax TypeScriptu i całe typowanie daje Nam dużo przydatnych udogodnień przez to kod jest krótszy i moim zdaniem nie mniej czytelny. :slight_smile:

1 Like

Rozmawialiśmy trochę o tym offline z Bartkiem i napotkaliśmy jeden problem - TypeScript nie robi sprawdzania typów w trakcie runtime’u. Co powoduje, że musimy wciąż robić ręczną walidację typów, co jest dosyć upierdliwe. Znalazłem kilka alternatyw:

Musimy ustalić, jakie problemy chcemy rozwiązywać. Moim zdaniem są to:

  1. Ręczne sprawdzanie typów argumentów i pisanie treści błędów w assert-ach jest upierdliwe. Aby rozwiązać ten problem potrzebujemy sprawdzania typów w trakcie runtime’u.
  2. Patrząc na kod funkcji jest czasem trudno odgadnąć, w jakiej postaci powinna otrzymywać ona argumenty. Ten problem rozwiąże dowolna notacja określająca typy argumentów itp. Możliwość statycznej analizy ułatwiającej integrację z IDE jest mile widziana, ale moim zdaniem nie jest konieczna.
  3. Trudno jest wykryć wszystkie niekompatybilne ze sobą wywołania funkcji, gdy robimy np. refaktor.. Tutaj statyczna analiza jest bardzo przydatna.

Jestem zdecydowanie za ^_^. Używałem w angularze i pisało się o wiele przyjemniej mogąc deklarować typy.

A jakiego typowania używałeś? Typescript, flow?

Typescript

1 Like

Jeszcze jest coś takiego: ts-runtime

Z tym deno to zastanawiam się jak wygląda wydajność i jak wygląda kwestia paczek npmowych. Niby pracuje nad tym jeden z twórców Node więc to raczej poważny projekt.

EDIT:
Ale ogólnie jestem za pójściem w tę stronę

1 Like

W trakcie prac nad Sealpage’em zostałem przekonany do Typescripta :smiley: W międzyczasie po cichu zacząłem próbę przepisywania Sealiousa na TS. Jak ktoś chciałby obczaić postęp, stworzyłem do tego brancha typescript.

Jak ktoś chce się udzielić w tym procesie (cc @piotr-ptaszynski) , zachęcam do zcheckoutowania się na ten branch i odpalenia

npm run build -- --watch

Pokaże się sterta błędów. Dużo z nich to po prostu „nie podałeś typu tego argumentu funkcji”. Zapraszam do rozwiązywania każdego z nich, nawet tych najprostszych :muscle: W razie pytań pytajcie proszę tutaj. Proszę też o dawanie znać tutaj jak ktoś się będzie zabierał za jakiś moduł, żebyśmy czasem nie robili roboty 2 razy.

Ja w najbliższym czasie będę dłubał field-types - więc ochotników proszę o kierowanie swoje wysiłki na pozostałe fragmenty kodu :wink:

2 Likes

To ja się zajmę obszarem subject, jeśli nikt nie ma nic na przeciwko

1 Like

Widzę, że TS czepia się konstrukcji:

import Promise from "bluebird";

mówiąc, że “Compiler reserves name Promise”. Widziałem w kodzie coś takiego:

import Bluebird from "bluebird";

tylko wyczuwam potencjalne problemy niekompatybilności promisów jak jeszcze czasem korzystamy z async… Jak do tego podchodzimy?