/*
Readonly je utility typ v TypeScriptu,
který změní všechny vlastnosti daného typu na pouze pro čtení (readonly)
Readonly<T>
*/
// 1
interface User {
id: number;
name: string;
email: string;
}
const user: Readonly<User> = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
};
// Pokus o změnu vlastnosti způsobí chybu
user.name = 'Bob'; // Error: Cannot assign to 'name' because it is a read-only property.
// 2
interface Config {
apiEndpoint: string;
timeout: number;
}
const config: Readonly<Config> = {
apiEndpoint: 'https://api.example.com',
timeout: 3000,
};
// Tento pokus o úpravu způsobí chybu:
config.timeout = 5000; // Error: Cannot assign to 'timeout' because it is a read-only property.
/*
Required je utility typ v TypeScriptu, který změní všechny vlastnosti daného typu na povinné (required).
*/
interface User1 {
id: number;
name?: string;
email?: string;
}
type RequiredUser = Required<User1>;
const user1: RequiredUser = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
};
// 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));
/*
Pick:
Pick<T, K> je utility typ v TypeScriptu, který vytváří nový typ výběrem určitých vlastností K z typu T.
Používá se pro vytvoření podmnožiny existujícího typu.
*/
// 1
interface User {
id: number;
name: string;
email: string;
phone: number;
}
type UserInfo = Pick<User, 'id' | 'name'>;
type UserBasicInfo = Pick<User, 'name' | 'email'>;
const user: UserInfo = {
id: 1,
name: 'abc',
};
const userDetails: UserBasicInfo = {
name: 'abc',
email: 'abc@gmail.com',
};
interface TestedPerson {
name: string;
age: number;
address: string;
email: string;
phone: string;
}
// 2 Kombinace Pick s jinými utility typy
type OptionalPersonBasicInfo = Partial<Pick<TestedPerson, 'name' | 'age'>>;
const partialBasicInfo: OptionalPersonBasicInfo = {
name: 'Bob',
// age může být vynecháno
};
// 3 Použití Pick s vnořenými objekty
interface ComplexPerson {
name: string;
age: number;
address: {
street: string;
city: string;
country: string;
};
}
type PersonNameAndCity = Pick<ComplexPerson, 'name'> & Pick<ComplexPerson['address'], 'city'>;
const nameAndCity: PersonNameAndCity = {
name: 'Charlie',
city: 'New York',
};
/*
Record<Keys, Type>
Keys může být string, number, symbol nebo union těchto typů.
Type může být jakýkoli typ.
Kdy Record využít
Když chcete zajistit, aby objekt měl přesnou strukturu s předem definovanými typy klíčů a hodnot.
Pokud například potřebujete typově bezpečně definovat mapování mezi hodnotami (např. slovníky nebo konfigurace).
*/
const scores: Record<string, number> = {
Alice: 10,
Bob: 20,
Charlie: 30,
};
type Person = 'name' | 'age' | 'email';
const personInfo: Record<Person, string> = {
name: 'Alice',
age: '30',
email: 'alice@example.com',
};
type Task = {
title: string;
completed: boolean;
};
type ProjectTasks = Record<string, Task>;
const projectStatus: ProjectTasks = {
'Task 1': { title: 'Design UI', completed: true },
'Task 2': { title: 'Implement backend', completed: false },
'Task 3': { title: 'Write tests', completed: false },
};
/*
Demo: https://jsbin.com/hugodahuge/1/edit?html,css,output
*/
.grid {
display: grid;
grid-template-rows: repeat(4, 1fr);
grid-auto-columns: calc((100vh - 3em) / 4);
grid-auto-flow: column;
grid-gap: 1em;
height: 100vh;
}
.grid-item:nth-child(3n) {
background-color: gray;
}
.grid-item:nth-child(3n + 1) {
background-color: green;
}
.grid-item:nth-child(3n + 2) {
background-color: yellow;
}
// https://blog.stackademic.com/how-to-optimize-complex-conditionals-in-javascript-0fcaf0add82a
const onButtonClick = (status) => {
if (status == 1) {
jumpTo('Index Page');
} else if (status == 2 || status == 3) {
jumpTo('Failure Page');
} else if (status == 4) {
jumpTo('Success Page');
} else if (status == 5) {
jumpTo('Cancel Page');
} else {
jumpTo('Other Actions');
}
};
// 1
// if => switch
const onButtonClick = (status) => {
switch (status) {
case 1:
jumpTo('Index Page');
break;
case 2:
case 3:
jumpTo('Failure Page');
break;
case 4:
jumpTo('Success Page');
break;
case 5:
jumpTo('Cancel Page');
break;
default:
jumpTo('Other Actions');
}
};
// ------------------------------------------
const onButtonClick = (status, identity) => {
if (identity == 'guest') {
if (status == 1) {
// logic for guest status 1
} else if (status == 2) {
// logic for guest status 2
}
// Additional logic for other statuses...
} else if (identity == 'master') {
if (status == 1) {
// logic for master status 1
}
// Additional logic for other statuses...
}
};
// 2
// interesting solution ;)
// nested if to map with keys where key mean concaten ifs..
const actions = new Map([
['guest_1', () => { /* logic for guest status 1 */ }],
['guest_2', () => { /* logic for guest status 2 */ }],
['master_1', () => { /* logic for master status 1 */ }],
['master_2', () => { /* logic for master status 2 */ }],
['default', () => { /* default logic */ }],
]);
const onButtonClick = (identity, status) => {
const action = actions.get(`${identity}_${status}`) || actions.get('default');
action();
};
// 3
// object with keys instead of use of ifs
const actions = {
'1': 'Index Page',
'2': 'Failure Page',
'3': 'Failure Page',
'4': 'Success Page',
'5': 'Cancel Page',
'default': 'Other Actions',
};
const onButtonClick = (status) => {
const action = actions[status] || actions['default'];
jumpTo(action);
};
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('Done!'), 1000);
promise.then(console.log); // 'Done!'
function foo()
{
const {promise, resolve, reject} = Promise.withResolvers()
setTimeout(() => {
resolve('DONE')
}, 2000)
return promise
}
function foo2()
{
const {promise, resolve, reject} = withResolvers()
setTimeout(() => {
resolve('DONE 2')
}, 2500)
return promise
}
// polyfill
function withResolvers() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
// Použití:
const { promise: aliasOnlyForTesting, resolve } = withResolvers();
(async() => {
const data = await foo()
console.log(data)
const data2 = await foo2()
console.log(data2)
})()
// 1 - composable
import { ref, computed, watchEffect } from 'vue';
import useSomeOtherComposable from './useSomeOtherComposable';
export default function useMyComposable() {
const { asyncValue1, asyncValue2 } = useSomeOtherComposable();
const resolved = ref(false);
// Hodnoty, které vrátíme, až budou splněny podmínky
const computedValue1 = ref(null);
const computedValue2 = ref(null);
// Proměnná promise, která se splní, když budou data připravena
const dataReady = new Promise((resolve) => {
watchEffect(() => {
if (asyncValue1.value === 'desiredValue1' && asyncValue2.value === 'desiredValue2') {
computedValue1.value = asyncValue1.value;
computedValue2.value = asyncValue2.value;
resolved.value = true;
resolve();
}
});
});
/*
// i watchEffect lze zastavit
const dataReady = new Promise((resolve) => {
const stopEffect = watchEffect(() => {
if (asyncValue1.value === 'desiredValue1' && asyncValue2.value === 'desiredValue2') {
computedValue1.value = asyncValue1.value;
computedValue2.value = asyncValue2.value;
resolve(); // Vyřeší Promise, když jsou hodnoty požadované
stopEffect(); // Zastaví watchEffect, protože už není potřeba
}
});
});
*/
// Vrátíme computed hodnoty i promise
return {
computedValue1,
computedValue2,
dataReady,
resolved
};
}
// usage - component
import { defineComponent, onMounted } from 'vue';
import useMyComposable from './useMyComposable'; // another composable which returns computed
export default defineComponent({
setup() {
const { computedValue1, computedValue2, dataReady } = useMyComposable();
onMounted(async () => {
await dataReady; // Počkáme, až budou hodnoty připravené
console.log('Values are ready:', computedValue1.value, computedValue2.value);
});
return {
computedValue1,
computedValue2
};
}
});
// 2 - watch insteadof watchEffect
import { ref, watch } from 'vue';
import useSomeOtherComposable from './useSomeOtherComposable';
export default function useMyComposable() {
const { asyncValue1, asyncValue2 } = useSomeOtherComposable();
const computedValue1 = ref(null);
const computedValue2 = ref(null);
const dataReady = new Promise((resolve) => {
const stopWatching = watch(
[asyncValue1, asyncValue2],
([newVal1, newVal2]) => {
if (newVal1 === 'desiredValue1' && newVal2 === 'desiredValue2') {
computedValue1.value = newVal1;
computedValue2.value = newVal2;
resolve(); // Vyřeší Promise, jakmile hodnoty odpovídají požadavkům
stopWatching(); // Zastaví sledování, protože už není potřeba
}
},
{ immediate: true } // Sleduj hned od začátku
);
});
return {
computedValue1,
computedValue2,
dataReady
};
}
// component usage
import { defineComponent, onMounted } from 'vue';
import useMyComposable from './useMyComposable';
export default defineComponent({
setup() {
const { computedValue1, computedValue2, dataReady } = useMyComposable();
onMounted(async () => {
await dataReady; // Počkáme, až budou hodnoty připravené
console.log('Values are ready:', computedValue1.value, computedValue2.value);
});
return {
computedValue1,
computedValue2
};
}
});
// ---------------------------------------------------
// geolocation checker aka MSP
// ---------------------------------------------------
import { ref, computed } from 'vue';
import { useMyComposable } from './path/to/composable';
export default {
setup() {
// Definuj proměnné na úrovni komponenty
const computedValue1 = ref(null);
const computedValue2 = ref(null);
const dataReady = ref(false);
const check = () => {
const { computedValue1: cv1, computedValue2: cv2, dataReady: dr } = useMyComposable();
// Přiřaď hodnoty z composable do definovaných proměnných
computedValue1.value = cv1.value;
computedValue2.value = cv2.value;
dataReady.value = dr.value;
};
// Spusť funkci nebo ji použij, když potřebuješ načíst data
check();
// Můžeš nyní přistupovat k computedValue1 a computedValue2 přímo mimo `check`
console.log(computedValue1.value, computedValue2.value);
return {
computedValue1,
computedValue2,
dataReady,
check,
};
},
};