typescripttypeormtype-inference

Type for TypeORM Raw Entity: CamelToSnake / ValueOf Exact Matching / Property-Method inference


Summary: Generating Types for Raw Return Types in TypeORM

Hello, I'm working with TypeORM and have a question about handling raw return types. When using getRawOne and getRawMany functions, the return includes strings composed of class names and properties, which are concatenated with an underscore ('_').

For example:

export class TestUser {
    id: number;
    name: string;
    verified: boolean;
}
const userRepository = getRepository(TestUser);

// getRawOne is not support type. just return any type. I want to disclose this return type.
const loaedRawTestUser = await userRepository.createQueryBuilder('testUser')
    .addSelect('COUNT(*()', 'myAliasForAllUserCount')
    .getRawOne();

/**
 * {
 *   testUser_id: number;
 *   testUser_name: string;
 *   testUser_verified: boolean;
 * }
 */
console.log(loaedRawUser);

The issue comprises three parts:

The first part has been resolved.

I've implemented a camelCase to snake_case type inferer as follows:

export type CamelCaseToSnakeCase<
    T extends string,
    Joiner extends '' | '_' = ''
> = T extends `${infer Character}${infer Rest}`
    ? Character extends Uppercase<Character>
        ? `${Joiner}${Lowercase<Character>}${CamelCaseToSnakeCase<Rest, '_'>}`
        : `${Character}${CamelCaseToSnakeCase<Rest, '_'>}`
    : '';

Is there something way?


Additional Requirements if it can be.

When TestUser class has method like this:

export class TestUser {
    id: number;
    name: string;
    verified: boolean;
    
    getNameIfVerified(): {
        return this.verified ? this.name : 'not verified'
    }
}

const typeormRawColumTestSample: Partial<TypeORMRawColumns<TestUser, 'TestUser'>> = {
    testUser_id: 1,
    //  when testUser_id: '1' should be error.
    testUser__get_name_if_verified // should be error.
}

How can I exclude methods from a Type in TypeScript


Solution

  • I've partially resolved the second issue, but it's not completely solved yet.

    /**
     * Not yet typescript is not support inferring class name from Type T.
     * So, I regard programmer must input entity class name as hardcoding. 
     */
    export type TypeORMRawColumns<
        Entity,
        EntityClassName extends string,
        ConvertedPropertyToSnake = `${Uncapitalize<EntityClassName>}_${CamelCaseToSnakeCase<
            Extract<keyof Entity, string>
        >}`,
        ValueType = Entity[keyof Entity]
    > = {
        [key in Extract<ConvertedPropertyToSnake, string>]: ValueType;
    };
    
    // Second parameter is hard coded for class name.
    const test1: TypeORMRawColumns<TestUser, 'TestUser'> = {
        testUser_id: 1,
        testUser_name: 'john',
        testUser_verified: true
    };
    
    // But by union type, it is not safe from other property type.
    // Expected error but it is not.
    const test2: TypeORMRawColumns<TestUser, 'TestUser'> = {
        testUser_id: 'it is not type-safe. Because of ValueOf is union type of Entity properties.',
        // like number | string | boolean
        testUser_name: 'john',
        testUser_verified: true
    };