```ts
export function topologicalSort(nodes: readonly string[], edges: readonly (readonly [string, string])[]): string[] {
    const graph = new Map<string, Set<string>>();
    const inDegree = new Map<string, number>();

    // Build the graph and calculate indegrees
    for (const [from, to] of edges) {
        if (!nodes.includes(from) || !nodes.includes(to)) {
            throw new Error("Edge mentions a node not in nodes");
        }
        graph.set(from, (graph.get(from) || new Set<string>()));
        graph.get(from)?.add(to);
        inDegree.set(to, (inDegree.get(to) || 0) + 1);
    }

    // Collect all nodes with no dependencies
    const freeNodes: string[] = [];
    for (const node of nodes) {
        if (!graph.has(node)) {
            freeNodes.push(node);
        }
    }

    const result: string[] = [];

    while (freeNodes.length > 0) {
        const currentNode = freeNodes.shift()!;
        result.push(currentNode);

        // Decrease the indegree of all dependent nodes
        for (const dependency of graph.get(currentNode) || []) {
            inDegree.set(dependency, inDegree.get(dependency)! - 1);
            if (inDegree.get(dependency) === 0) {
                freeNodes.push(dependency);
            }
        }
    }

    // Check for cycles
    const visited = new Set<string>();
    function dfs(node: string): void {
        visited.add(node);
        for (const dependency of graph.get(node) || []) {
            if (!visited.has(dependency)) {
                dfs(dependency);
            } else {
                throw new Error("Cycle detected");
            }
        }
    }

    // Perform a DFS to check for cycles
    for (const node of nodes) {
        if (!visited.has(node)) {
            dfs(node);
        }
    }

    return result;
}
```