I want totally immutable object in TS

We now have the option as const which is a syntactically concise way of what @phil294 mentioned as the first option (nested readonly).

const a = {
    b: 33,
    c: [78, 99],
    d:{e:{f:{g:true, h:{boom:'selecta'}}}}
} as const;

a.d.e.f.h.boom = 'respek'; //Cannot assign to 'boom' because it is a read-only property.ts(2540)

As an added bonus, you can make inputs to functions nested immutable using this trick:

type Immutable<T> = {
    readonly [K in keyof T]: Immutable<T[K]>;
}

so this would happen

const a = {
    b: 33,
    c: [78, 99],
    d:{e:{f:{g:true, h:{boom:'selecta'}}}}
}

function mutateImmutable(input: Immutable<typeof a>) {
    input.d.e.f.h.boom = 'respek'; //Cannot assign to 'boom' because it is a read-only property.ts(2540)
}

As described in https://www.typescriptlang.org/docs/handbook/interfaces.html, you can use readonly on class/interface properties or Readonly<...>/ReadonlyArray<> for immutable objects and arrays. In your case, this would look like the following:

const a: Readonly<{
    b: number,
    c: ReadonlyArray<number>,
    d: Readonly<{
        e: Readonly<{
            f: Readonly<{
                g: boolean,
                h: Readonly<{
                    boom: string
                }>
            }>
        }>
    }>
}> = {
        b: 33,
        c: [78, 99],
        d:{e:{f:{g:true, h:{boom:'selecta'}}}}
}

a.d.e.f.h.boom = 'respek'; // error: Cannot assign to 'boom' because it is a constant or a read-only property.

Obviously, this is quite the tautological statement, so I suggest you define proper class structure for your object. You are not really taking advantage of any of Typescript's features by just declaring a nested, untyped object.

But if you really need to go without type definitions, I think the only way is defining a freezer (love the term :D) like Hampus suggested. Taken from deepFreeze(obj) function from MDN:

function freezer(obj) {
    Object.getOwnPropertyNames(obj).forEach(name => {
        if (typeof obj[name] == 'object' && obj[name] !== null)
            freezer(obj[name]);
    });
    return Object.freeze(obj);
}

const a = freezer({
    b:33, 
    c:[78, 99], 
    d:{e:{f:{g:true, h:{boom:'selecta'}}}}});

a.d.e.f.h.boom='respek'; // this does NOT throw an error. it simply does not override the value.

tl;dr: You cannot get compiler type errors without defining types. That is the whole point of Typescript.

edit:

this very last statement is wrong. For example,

let a = 1
a = "hello"

will throw an error because the type is implicitly set to number. For readonly however, I think, you will need proper declaration as defined above.