【发布时间】:2021-07-27 02:46:04
【问题描述】:
编辑:我已经更新了问题底部的工作代码。
我有一个名为get 的函数,它接受key 作为参数并返回相应的
价值。
此函数为每个key 维护value(始终是字符串)和datatype。
喜欢:
key value(stored as string) dataType
-------------------------------------------------
count '10' number
name 'foo' string
isHoliday '1' boolean
===== more rows here =====
在返回值之前,它会转换为指定的数据类型。
例如:
- 当key为
count时,其值'10'被转换为数据类型number,值为10 - 当key为
isHoliday时,其值'1'被转换为数据类型boolean,值为true
get 函数以正确的类型返回正确的值。
但是 typescript 在名为 place.const count: number = get('count')
并产生类型错误,如:Type 'string | number | boolean' is not assignable to type 'number'.
代码:
type TypeName = 'number' | 'string' | 'boolean'
type ItemKey = 'count' | 'name' | 'isHoliday'
function get(key: ItemKey) {
/**
* Predefined data
*/
const data: {
[key: string]: {
value: string
dataType: TypeName
}
} = {
count : { value: '10', dataType: 'number' },
name : { value: 'foo', dataType: 'string' },
isHoliday : { value: '1', dataType: 'boolean' },
// ===== more items here =====
}
/**
* Fetch data based on input
*/
const value = data[key].value // fetch value
const dataType = data[key].dataType // fetch TypeName
/**
* Cast data type and return value
*/
if(dataType === 'number') {
return Number(value)
} else if(dataType === 'string') {
return String(value)
} else if(dataType === 'boolean') {
return value === '1'
}
}
/**
* Using get() funciton here
*/
const count: number = get('count')
// Error: Type 'string | number | boolean' is not assignable to type 'number'.
const isHoliday: boolean = get('isHoliday')
// Error: Type 'string | number | boolean' is not assignable to type 'boolean'.
我试过TypeScript: function return type based on argument, without overloading。
但我未能转换数据类型。
interface TypeRegistry {
count: number
name: string
isHoliday: boolean
// more items here
}
function get<K extends keyof TypeRegistry>(key: K): TypeRegistry[K] {
const data: {
[key: string]: {
value: string
}
} = {
count : { value: '10' },
name : { value: 'foo' },
isHoliday : { value: '1' },
// more items here
}
const value = data[key].value // fetch value
// How to cast value to TypeRegistry[K]???
return value
// Error: Type 'string' is not assignable to type 'TypeRegistry[K]'.
}
/**
* Using get() funciton here
*/
const count: number = get('count')
const isHoliday: boolean = get('isHoliday')
This answer 使用条件类型,但他们使用key 来决定数据类型。
但在我的情况下,键的数量可能会有所不同。
我希望只使用 TypeNames 而不是键的东西。
但是这种方法也没有成功
代码:
type TypeName = 'number' | 'string' | 'boolean'
type ItemKey = 'count' | 'name' | 'isHoliday'
type ReturnDataType<T> =
T extends 'count' ? number :
T extends 'name' ? string :
T extends 'isHoliday' ? boolean:
// more items here
never;
function get<T extends ItemKey>(key: ItemKey): ReturnDataType<T> {
/**
* Predefined data
*/
const data: {
[key: string]: {
value: string
dataType: TypeName
}
} = {
count : { value: '10', dataType: 'number' },
name : { value: 'foo', dataType: 'string' },
isHoliday : { value: '1', dataType: 'boolean' },
// more items here
}
/**
* Fetch data based on input
*/
const value = data[key].value // fetch value
const dataType = data[key].dataType // fetch TypeName
/**
* Cast data type and return value
*/
if(dataType === 'number') {
return Number(value) as ReturnDataType<T>
} else if(dataType === 'string') {
return String(value) as ReturnDataType<T>
} else if(dataType === 'boolean') {
return Boolean(value) as ReturnDataType<T>
}
}
/**
* Using get() funciton here
*/
const count: number = get('count')
// Error:
// Type 'string | number | boolean' is not assignable to type 'number'.
// Type 'string' is not assignable to type 'number'.ts(2322)
const isHoliday: boolean = get('isHoliday')
// Error:
// Type 'string | number | boolean' is not assignable to type 'boolean'.
// Type 'string' is not assignable to type 'boolean'.ts(2322)
编辑:从 jcalz answer,我想出了以下代码,它可以正常工作
/**
* Data type map
* Usefull only at compile time
*/
type DataTypeMap = {
'number': number
'string': string
'boolean': boolean
}
/**
* For typescript type hint (compile time)
*/
type TsTypeRegistry = {
count : number
name : string
isHoliday : boolean
// ===== more items here =====
}
/**
* Type required at runtime
*/
const JsTypeRegistry: {
[key: string]: keyof DataTypeMap
} = {
count : 'number',
name : 'string',
isHoliday : 'boolean',
// ===== more items here =====
}
/**
* Convert datatype at runtime
*/
function coerce(value: string, type: keyof DataTypeMap) {
switch(type) {
case 'number': return Number(value)
case 'string': return value
case 'boolean': return value === '1'
default: throw new Error(`coerce not implemented for type: '${type}'`)
}
}
/**
* The get function
*/
function get<K extends keyof TsTypeRegistry>(key: K): TsTypeRegistry[K] {
const data = {
count: { value: '10' },
name: { value: 'foo' },
isHoliday: { value: '1' },
// ===== more items here =====
}
const value = data[key].value
const dataType = JsTypeRegistry[key] // datatype required at runtime
return coerce(value, dataType) as TsTypeRegistry[K]
/**
* Here 'coerce' converts value at runtime
* and 'TsTypeRegistry[K]' gives correct type at compile time
*/
}
/**
* Using get function
*/
const count: number = get('count')
console.log(count, typeof count) // 10 'number'
const someName: string = get('name')
console.log(someName, typeof someName) // foo 'string'
const isHoliday: boolean = get('isHoliday')
console.log(isHoliday, typeof isHoliday) // true 'boolean
/**
* Now the get function converts data type at runtime as well as
* gives correct type hint at compile time.
*/
【问题讨论】:
标签: typescript casting return-type