1

Given the following sample

interface DataProvider {
    type: string;
    // other props
}

interface ApiConfiguration {
    dataProvider: DataProvider;
    // other props
}

interface Configuration {
    api: ApiConfiguration;
    // other props
}

const configuration: Configuration = {
    api: {
        dataProvider: { type: 'http' }
    }
};

This configuration will be validated against a schema. For a given test I want to ensure a validation error will be thrown if the type field is missing.

delete configuration.api.dataProvider.type

is not possible because

The operand of a 'delete' operator must be optional.

since the interface must have the type prop. I am aware that there are Pick and Omit, but creating a custom interface type for each test case would be very time-consuming.

Currently I'm using this approach

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const clonedConfiguration: any = structuredClone(configuration);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {type: _, ...dataProviderWithoutType} = clonedConfiguration.api.dataProvider;

clonedConfiguration.api.dataProvider = dataProviderWithoutType;

but is there a more elegant way to remove props from nested children?

baitendbidz
  • 187
  • 3
  • 19

2 Answers2

1

You can use a small utility to omit the type property in your nested type.

type ChangeFields<T, R> = Omit<T, keyof R> & R;

type TestConfig = ChangeFields<Configuration, {
    api: ChangeFields<ApiConfiguration, {
        dataProvider: ChangeFields<DataProvider, {
            type?: string
        }>
    }>
}>

const withoutType = (obj: Configuration): TestConfig  => {
    const res = obj as TestConfig; //deepclone if used elsewhere
    delete res.api.dataProvider.type;
    return res;
}

const c = withoutType(configuration);

As you only need this to write a test it probably won't be an issue, but you should properly deepclone the object when you're deleting a nested property if it's referenced in other places. Otherwise you would modify a differently typed object through the reference.

Moritz Roessler
  • 8,542
  • 26
  • 51
0

You can use the as keyword from Typescript to do this. When using it, you explicitly tell Typescript not to worry about what's inside the object.

See this question for details.

interface DataProvider {
    type: string;
    // other props
}

interface ApiConfiguration {
    dataProvider: DataProvider;
    // other props
}

interface Configuration {
    api: ApiConfiguration;
    // other props
}

const configuration: Configuration = {
    api: {
        dataProvider: {} as DataProvider,
    }
};
  • 1
    I didn't downvote, but better not declare that object as a `Configuration` when it isn't one. – Bergi Jan 25 '23 at 11:08
  • @Bergi In the "runtime" or "application" code indeed, this is not a good practice. However OP is writing a test in which they explicitly want to simulate wrong typed data. In my opinion, declaring here `{}` as a `Configuration` is appropriate. – clementlize Jan 25 '23 at 11:15
  • 3
    @clementlize I disagree. I think that the types of the validation function the OP wants to call should explicitly reflect that it may be called with invalid data. – Bergi Jan 25 '23 at 11:24
  • 1
    I downvoted because it's not a clean solution. It covers up the fact, that type is optional. It's a workaround not a solution. – Moritz Roessler Jan 25 '23 at 11:28