从通用类型创建新类型
Create new type from generic type
我正在尝试基于通用类型创建新的对象类型,但我的打字稿一直说它是不正确的类型。
例如:我想将所有对象属性转换为字符串。
-- 编辑示例使其更完整
条目类型
interface Example {
value1: string;
value2: number;
value3: {
value4: boolean
}
}
// Expected new type
interface NewExample {
value1: string;
value2: string;
value3: {
value4: string
}
}
我当前的函数基本上将所有对象属性转换为字符串:(假设没有数组)
public convertObject<T>(obj: T) {
Object.keys(obj).forEach( prop => {
const value = obj[prop];
if(typeof value === 'object')
obj[prop] = this.convertObject(obj[prop])
else
obj[prop] = 'example'+obj[prop]
})
return obj;
}
鉴于我将函数应用于已定义的对象,我想要的只是任何 属性 的每个“typeof”都是一个字符串。
我怎样才能做到这一点?
编辑:
let a = { _number: 1, _obj: {mock: true} };
let b = this.convertObject(a);
b._obj; // This should me an object
b._obj.mock; // This should me a string
type ValueOf<O> = O[keyof O];
function convertObject<O extends Record<string, any>, V extends ValueOf<O>>(
obj: O
): V extends Record<any, any>
? Record<keyof O, Record<keyof V, string>>
: Record<keyof O, string> {
(Object.keys(obj) as Array<keyof O>).forEach((prop) => {
const value = obj[prop];
if (typeof value === 'object') {
obj[prop] = convertObject(obj[prop]) as ValueOf<O>;
} else {
obj[prop] = ('example' + obj[prop]) as ValueOf<O>;
}
});
return obj;
}
const hello: Example = {
value1: 'fdsafd',
value2: 444,
value3: {
value4: true
},
another: {
thing: 3213
}
};
const thing = convertObject(hello);
thing
应该在那里正确输入,每个值都是一个字符串。唯一的问题是你必须添加 as
语句,否则 TS 编译器会抱怨类型不兼容。
您可以将这种类型转换表达如下:
type ConvertObject<T> =
T extends object ? { [K in keyof T]: ConvertObject<T[K]> } : string;
这是一个 conditional type which checks if its input type T
is an object, and if so, evaluates to mapped type,其中每个 属性 都被递归转换;如果不是,则计算结果为 string
。您可以验证它是否对 Example
:
做了正确的事情
type NewExample = ConvertObject<Example>;
/* type NewExample = {
value1: string;
value2: string;
value3: {
value4: string;
};
} */
对于 convertObject()
的实际实现,需要 hard/impossible 让编译器相信您正在做的是类型安全的(参见 microsoft/TypeScript#33912 for the current state of affairs with implementing functions whose call signatures are generic conditional types), so you will probably need to use type assertions 或等价物以放松类型检查在实现内部足以避免编译器错误。
这是一种方法:
function convertObject<T extends object>(obj: T): ConvertObject<T> {
const ret: any = {};
Object.keys(obj).forEach(prop => {
const value = (obj as any)[prop];
ret[prop] = typeof value === 'object' ? convertObject(value) : ('example ' + value);
})
return ret;
}
请注意,我已将其设为独立函数(而不是 class 方法),并且它不会改变传入的 obj
,但 returns 一个新对象 ret
。 TypeScript 的类型系统确实无法模拟类型(例如)number
更改为 string
的情况。因此,如果您调用 convertObject(obj)
,编译器看到的 obj
的类型将不会改变,并且您 运行 如果您确实更改了 运行 时间错误的风险obj
的类型。所以我建议避免这种突变;如果你真的需要改变传入的obj
,你应该非常小心,以后不要再使用obj
。
还要注意实现中的类型松动;我将 obj
视为 any
并使 ret
成为 any
类型,这样编译器就不会报错。这将维护类型安全的负担放在了我(或你)身上,因此我们应该检查并仔细检查 convertObject()
在其输入类型为 [=14= 时确实产生了类型 ConvertObject<T>
的输出].
让我们测试一下是否有效:
let a = { _number: 1, _obj: { mock: true } };
let b = convertObject(a);
/* let b: {
_number: string;
_obj: {
mock: string;
};
} */
console.log(b._number.toUpperCase()); // EXAMPLE 1
console.log(b._obj.mock.toUpperCase()); // EXAMPLE TRUE
看起来不错。编译器认为 b
的类型正是您想要的,并且实现也有效。
我正在尝试基于通用类型创建新的对象类型,但我的打字稿一直说它是不正确的类型。 例如:我想将所有对象属性转换为字符串。
-- 编辑示例使其更完整 条目类型
interface Example {
value1: string;
value2: number;
value3: {
value4: boolean
}
}
// Expected new type
interface NewExample {
value1: string;
value2: string;
value3: {
value4: string
}
}
我当前的函数基本上将所有对象属性转换为字符串:(假设没有数组)
public convertObject<T>(obj: T) {
Object.keys(obj).forEach( prop => {
const value = obj[prop];
if(typeof value === 'object')
obj[prop] = this.convertObject(obj[prop])
else
obj[prop] = 'example'+obj[prop]
})
return obj;
}
鉴于我将函数应用于已定义的对象,我想要的只是任何 属性 的每个“typeof”都是一个字符串。
我怎样才能做到这一点?
编辑:
let a = { _number: 1, _obj: {mock: true} };
let b = this.convertObject(a);
b._obj; // This should me an object
b._obj.mock; // This should me a string
type ValueOf<O> = O[keyof O];
function convertObject<O extends Record<string, any>, V extends ValueOf<O>>(
obj: O
): V extends Record<any, any>
? Record<keyof O, Record<keyof V, string>>
: Record<keyof O, string> {
(Object.keys(obj) as Array<keyof O>).forEach((prop) => {
const value = obj[prop];
if (typeof value === 'object') {
obj[prop] = convertObject(obj[prop]) as ValueOf<O>;
} else {
obj[prop] = ('example' + obj[prop]) as ValueOf<O>;
}
});
return obj;
}
const hello: Example = {
value1: 'fdsafd',
value2: 444,
value3: {
value4: true
},
another: {
thing: 3213
}
};
const thing = convertObject(hello);
thing
应该在那里正确输入,每个值都是一个字符串。唯一的问题是你必须添加 as
语句,否则 TS 编译器会抱怨类型不兼容。
您可以将这种类型转换表达如下:
type ConvertObject<T> =
T extends object ? { [K in keyof T]: ConvertObject<T[K]> } : string;
这是一个 conditional type which checks if its input type T
is an object, and if so, evaluates to mapped type,其中每个 属性 都被递归转换;如果不是,则计算结果为 string
。您可以验证它是否对 Example
:
type NewExample = ConvertObject<Example>;
/* type NewExample = {
value1: string;
value2: string;
value3: {
value4: string;
};
} */
对于 convertObject()
的实际实现,需要 hard/impossible 让编译器相信您正在做的是类型安全的(参见 microsoft/TypeScript#33912 for the current state of affairs with implementing functions whose call signatures are generic conditional types), so you will probably need to use type assertions 或等价物以放松类型检查在实现内部足以避免编译器错误。
这是一种方法:
function convertObject<T extends object>(obj: T): ConvertObject<T> {
const ret: any = {};
Object.keys(obj).forEach(prop => {
const value = (obj as any)[prop];
ret[prop] = typeof value === 'object' ? convertObject(value) : ('example ' + value);
})
return ret;
}
请注意,我已将其设为独立函数(而不是 class 方法),并且它不会改变传入的 obj
,但 returns 一个新对象 ret
。 TypeScript 的类型系统确实无法模拟类型(例如)number
更改为 string
的情况。因此,如果您调用 convertObject(obj)
,编译器看到的 obj
的类型将不会改变,并且您 运行 如果您确实更改了 运行 时间错误的风险obj
的类型。所以我建议避免这种突变;如果你真的需要改变传入的obj
,你应该非常小心,以后不要再使用obj
。
还要注意实现中的类型松动;我将 obj
视为 any
并使 ret
成为 any
类型,这样编译器就不会报错。这将维护类型安全的负担放在了我(或你)身上,因此我们应该检查并仔细检查 convertObject()
在其输入类型为 [=14= 时确实产生了类型 ConvertObject<T>
的输出].
让我们测试一下是否有效:
let a = { _number: 1, _obj: { mock: true } };
let b = convertObject(a);
/* let b: {
_number: string;
_obj: {
mock: string;
};
} */
console.log(b._number.toUpperCase()); // EXAMPLE 1
console.log(b._obj.mock.toUpperCase()); // EXAMPLE TRUE
看起来不错。编译器认为 b
的类型正是您想要的,并且实现也有效。