/ Gists / Typescript

Gists - Typescript

On gists

Better Vue components with TS

JavaScript Typescript Vue.js

better-vue-with-components.vue #

<!-- 1. PROPS  -->

<!-- JS -->
<script setup>
import { defineProps } from 'vue';

const props = defineProps({
  foo: { type: String, required: true },
  bar: Number,
});

// props.foo is a string
// props.bar is a number or undefined
</script>


<!-- TS -->
<script setup lang="ts">
const props = defineProps<{
  foo: string;
  bar?: number;
}>()
</script>


<script setup lang="ts">
interface Props {
  foo: string;
  bar?: number;
}

const props = defineProps<Props>()
</script>



<!-- 2. EMITS  -->
<!-- JS -->
<script setup>
const emit = defineEmits(['change', 'update'])
</script>

 <!-- TS -->
 <script setup lang="ts">
const emit = defineEmits<{
  change: [id: number]
  update: [value: string]
}>()
</script>



<!-- 3. Typing Ref and Reactive Data => Inference -->
<script setup>
import { ref } from 'vue';

const count = ref(0); 
count.value = 'string';  // error, inference, we expect number
</script>


<!-- 4. Typing Server Responses -->
<script setup lang="ts">
import { ref, onMounted } from 'vue';

interface User {
  id: number;
  name: string;
  email: string;
}

const userData = ref<User | null>(null);

onMounted(async () => {
  const response = await fetch('https://api.example.com/user');
  const data: User = await response.json();
  userData.value = data; // TypeScript ensures data usages match the User interface
});
</script>


<!-- 5. Typing Computed Data -->
import { ref, computed } from 'vue'

const count = ref(0)
// inferred type: ComputedRef<number>
const double = computed(() => count.value * 2)
// => TS Error: Property 'split' does not exist on type 'number'
const result = double.value.split('')

// or explicitely define return type
const double = computed<number>(() => {
  // type error if this doesn't return a number
})



<!-- 6. Typing Scoped Slots-->
<template>
  <slot :msg="message"></slot>
</template>

<script setup lang="ts">
const message = 'Hello, Vue!';

const slots = defineSlots<{
  default: (props: { msg: string }) => any;
}>()
</script>


<!-- 7. Typing Template Refs -->
<script setup lang="ts">
import { useTemplateRef, onMounted } from 'vue';

const myInput = useTemplateRef('my-input');

onMounted(() => {
  myInput.value?.focus(); 
});
</script>

<template>
  <input ref="my-input" />
</template>



<!-- 8. Typing Provide/Inject -->
<!-- ParentComponent.vue -->
<script setup lang="ts">
import { provide } from 'vue';

const theme = 'dark';
provide('theme', theme);
</script>

<!-- ChildComponent.vue -->
<script setup lang="ts">
import { inject } from 'vue';

const theme = inject<string>('theme'); // Inject with expected type
// TypeScript ensures that 'theme' is of type string
</script>


<!-- 9. Generics -->
<script setup lang="ts" generic="T">
defineProps<{
  items: T[];      // Array of items of type T
  selected: T;     // Single selected item of type T
}>()
</script>


<!-- 10. Typed Composables -->
// useUser.ts
import { ref } from 'vue';

interface User {
  id: number;
  name: string;
  age: number;
}

export function useUser() {
  const user = ref<User | null>(null);

  function fetchUser(id: number) {
    // Fetching user logic
    user.value = { id, name: 'John Doe', age: 30 };
  }

  return { user, fetchUser };
}

On gists

Template literals

Typescript

index.ts #

type Pozdrav = `ahoj${string}`;
let ok: Pozdrav = "ahojsvete";     // ✅
let nok: Pozdrav = "nazdar";       // ❌ musí začínat "ahoj"



// -------------------- //
type Velikost = "small" | "medium" | "large";
type Barva = "red" | "blue";
type TrickoSVelikosti = `${Velikost}-${Barva}`;

let tricko: TrickoSVelikosti = "small-red";    // ✅
let tricko2: TrickoSVelikosti = "small-green"; // ❌ green není v typu Barva



// -------------------- //
// Uppercase/Lowercase - převod na velká/malá písmena
type Pozdrav = "ahoj" | "nazdar";
type KriciciPozdrav = `${Uppercase<Pozdrav>}!`;  // typ je "AHOJ!" | "NAZDAR!"

// Capitalize - první písmeno velké
type Capitalize<S extends string> = `${Uppercase<S[0]>}${Lowercase<S>}`;
type Jmeno = Capitalize<"petr">;  // typ je "Petr"




// -------------------- //
// string s přesnou délkou 2
type DvaPismena = string & { length: 2 };
type Status = `${DvaPismena}-${number}`;

let ok: Status = "OK-123";    // ✅
let nok: Status = "OK1-123";  // ❌ první část musí mít délku 2




// -------------------- //
type Email = `${string}@${string}.${string}`;
let email: Email = "test@example.com";   // ✅
let neplatny: Email = "testexample.com"; // ❌ chybí @




// -------------------- //
// Definujeme povolené hex znaky
type HexDigit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" 
               | "a" | "b" | "c" | "d" | "e" | "f";

// Přesná definice hex barvy
type HexColor = `#${HexDigit}${HexDigit}${HexDigit}${HexDigit}${HexDigit}${HexDigit}`;

let barva1: HexColor = "#ff00ff";  // ✅
let barva2: HexColor = "#ff00";    // ❌ příliš krátké
let barva3: HexColor = "#gg0000";  // ❌ 'g' není hex digit

On gists

Generic constract vs Utility type

Typescript

comparison.ts #

// 1. Primitivní typy - lze zjednodušit
// S generics:
function processValue<T extends string>(value: T) { }
// Jednodušeji:
function processValue(value: string) { }

// 2. Union typy - lze zjednodušit
type Fruit = "apple" | "banana" | "orange";
// S generics:
function processFruit<T extends Fruit>(fruit: T) { }
// Jednodušeji:
function processFruit(fruit: Fruit) { }

// 3. Literal typy - lze zjednodušit
// S generics:
function processLiteral<T extends "yes" | "no">(answer: T) { }
// Jednodušeji:
function processLiteral(answer: "yes" | "no") { }

// 4. Array typy - lze zjednodušit v základním případě
// S generics:
function processArray<T extends any[]>(arr: T) { }
function processStringArray<T extends string[]>(arr: T) { }
// Jednodušeji:
function processArray(arr: any[]) { }
function processStringArray(arr: string[]) { }

// 5. Function typy - lze zjednodušit
type Logger = (msg: string) => void;
// S generics:
function withLogging<T extends Logger>(logger: T) { }
// Jednodušeji:
function withLogging(logger: Logger) { }

// 6. Class typy - lze zjednodušit v základním případě
class Animal { }
// S generics:
function processClass<T extends Animal>(instance: T) { }
// Jednodušeji:
function processClass(instance: Animal) { }

// 7. Generický typ s extends - POUZE PŘES GENERIC CONSTRAINT
interface Box<T> {
    value: T;
}
interface NumberBox<T extends number> extends Box<T> {
    increment(): void;
}

// 8. Kombinace více typů - lze zjednodušit
type StringWithLength = string & { length: number };
// S generics:
function processString<T extends string & { length: number }>(value: T) { }
// Jednodušeji:
function processString(value: StringWithLength) { }

// 9. Conditional type - POUZE PŘES GENERIC CONSTRAINT
type NonNullable<T> = T extends null | undefined ? never : T;


On gists

Generic constraint

Typescript

index.ts #

/*
omezení, která můžeme přidat na generické typy, aby splňovaly určité podmínky.
*/

// T musí být objekt
function getProperty<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

// Použití:
const person = { name: "Jan", age: 30 };
const name = getProperty(person, "name"); // OK
const invalid = getProperty(person, "invalid"); // Chyba - 'invalid' není klíč v person

On gists

Assertion / Casting

Typescript

index.ts #

// Casting = přetypování v TS nelze
// Asserce je explicitni určený typu

/*
  : = type annotation (deklarace typu)
  as / <> = type assertion (casting)
  <T> = generic type parameter (generika)
*/

// Oba tyto řádky dělají totéž - type assertion/casting
let value: any = "hello";
let length1 = (value as string).length;        // "as" syntax
let length2 = (<string>value).length;          // "angle bracket" syntax



/ 1. Type annotation
let myStr: string = "hello";

// 2. Type assertion (as) - když TS neví typ
let someValue: any = "hello";
let strLength = (someValue as string).length;

// 3. Type assertion (angle brackets) - stejné jako 2
let strLength2 = (<string>someValue).length;

// 4. Generic type parameter
let myObs: Observable<number>;


let x: unknown;
x = [];

// 1
let result = (x as number[]).push(111);

// 2
let result = (<number[]>x).push(111);

On gists

Overview

Typescript

index.md #

Utility Typ Použití na Akce (Whitelist/Blacklist) Popis Podobné Funkce
Pick Objekty Whitelist Vybere určité klíče z objektu a vytvoří nový typ. Extract
Omit Objekty Blacklist Odstraní specifikované klíče z objektu a vytvoří nový typ. Exclude
Partial Objekty - Změní všechny vlastnosti objektu na volitelné. -
Required Objekty - Změní všechny vlastnosti objektu na povinné. -
Readonly Objekty - Změní všechny vlastnosti objektu na pouze pro čtení. -
Record Typy - Vytvoří typ objektu s konkrétními klíči a hodnotami daného typu. -
Exclude Typy Blacklist Odstraní určité hodnoty z union typu. Omit
Extract Typy Whitelist Vybere pouze specifikované hodnoty z union typu. Pick
NonNullable Typy Blacklist Odstraní null a undefined z typu. -

On gists

Types

Typescript

types.ts #

// union
type StringOrNumber = string | number;

// intersection
type User = { id: number };
type Admin = { isAdmin: boolean };
type AdminUser = User & Admin; // { id: number; isAdmin: boolean; }

// return type
type GetUserType = () => { id: number; name: string };
type UserType = ReturnType<GetUserType>; // { id: number; name: string }

// return type pres interface
interface StringFormat {
    (str: string, isUpper: boolean): string;
}

let format: StringFormat;

format = function (str: string, isUpper: boolean) {
    return isUpper ? str.toLocaleUpperCase() : str.toLocaleLowerCase();
};

console.log(format('hi', true));

On gists

Utility type: Nonnullable

Typescript

index.ts #

/*

NonNullable je utility typ v TypeScriptu, který odstraní null a undefined z daného typu. To znamená, že výsledný typ bude obsahovat všechny hodnoty z původního typu kromě null a undefined

NonNullable<T>
*/

// 1
type UserInput = string | number | null | undefined;

function processUserInput(input: NonNullable<UserInput>) {
    console.log(`Processing user input: ${input}`);
}

processUserInput('Hello');
processUserInput(42);

// Below would cause compile-time errors:
processUserInput(null);
processUserInput(undefined);

On gists

Utility type: Extract

Typescript

index.ts #

/*
Extract je utility typ v TypeScriptu, který umožňuje vytvořit nový typ extrahováním (vybráním) hodnot, které jsou společné mezi dvěma typy. Funguje jako opak Exclude, protože místo odstranění hodnot vybírá ty, které se shodují. Syntaxe vypadá takto:

Extract<T, U>

*/

// 1
type Status = 'active' | 'inactive' | 'pending' | 'deleted';
type ActiveStatus = Extract<Status, 'active' | 'pending'>;

let status1: ActiveStatus = 'active'; // OK
let status2: ActiveStatus = 'pending'; // OK
let status3: ActiveStatus = 'inactive'; // Error: Type '"inactive"' is not assignable to type 'ActiveStatus'.

// 2
type Numeric = number | string | boolean;
type OnlyString = Extract<Numeric, string>;

const value1: OnlyString = 'Hello'; // OK
const value2: OnlyString = 42; // Error: Type 'number' is not assignable to type 'OnlyString'.

On gists

Utility type: Exclude

Typescript

index.ts #

/*
Exclude<T, K>
Exclude je utility typ v TypeScriptu, který umožňuje vytvořit nový typ odstraněním určitých hodnot z existujícího typu. Tento typ pracuje především s tzv. union typy (sjednocení). Jeho syntaxe vypadá takto:

*/

// 1
type Status = 'active' | 'inactive' | 'pending' | 'deleted';

type VisibleStatus = Exclude<Status, 'deleted'>;

let status1: VisibleStatus = 'active'; // OK
let status2: VisibleStatus = 'inactive'; // OK
let status3: VisibleStatus = 'deleted'; // Error: Type '"deleted"' is not assignable to type 'VisibleStatus'.

// 2
type StringOrNull = string | null | undefined;

type StringOnly = Exclude<StringOrNull, null | undefined>;

const value1: StringOnly = 'Hello'; // OK
const value2: StringOnly = null; // Error: Type 'null' is not assignable to type 'StringOnly'.
const value3: StringOnly = undefined; // Error: Type 'undefined' is not assignable to type 'StringOnly'.