如何告诉TypeScript对象中存在可选属性并已设置?
我有以下代码:如何告诉TypeScript对象中存在可选属性并已设置?,typescript,Typescript,我有以下代码: 接口优先 { 属性a:字符串; } //这里propertyA是可选的 //假设此接口来自外部库。 接口秒 { propertyA?:字符串; } 函数fn(参数:第一个) { // ... } //我知道这个对象可以声明为First的类型, //但我真的需要将其设置为第二种接口类型 让myVar:Second={propertyA:'some string'}; //我真的需要用这种方式打电话。 fn(myVar);//错误 if(myVar.hasOwnProperty('p
接口优先
{
属性a:字符串;
}
//这里propertyA是可选的
//假设此接口来自外部库。
接口秒
{
propertyA?:字符串;
}
函数fn(参数:第一个)
{
// ...
}
//我知道这个对象可以声明为First的类型,
//但我真的需要将其设置为第二种接口类型
让myVar:Second={propertyA:'some string'};
//我真的需要用这种方式打电话。
fn(myVar);//错误
if(myVar.hasOwnProperty('propertyA'))
{
fn(myVar);//仍然是相同的错误
}
if(myVar.propertyA)
{
fn(myVar);//仍然是相同的错误
}
但是类型脚本抛出错误:
类型为“Second”的参数不能分配给类型为“First”的参数。
属性“propertyA”在类型“Second”中是可选的,但在类型“First”中是必需的
那么,如何告诉TypeScript,
myVar
中的可选属性propertyA
存在并已设置?我不明白为什么您将其声明为typeSecond
,因为它具有该属性。但是,您可以执行以下操作之一:
- 将声明中的类型更改为
,即First
让myVar:First={propertyA:'some string'}代码>
- 完全删除类型声明。然后,它将接收一个匿名类型
,并将首先分配给{propertyA:string;}
,即让myVar={propertyA:'some string'}代码>
- 使用显式类型转换,即
fn(myVar)代码>
fn(myVar as First);
并在以下情况下使用a:
function isFirst(obj: any): obj is First {
return obj && obj.propertyA;
}
if(isFirst(myVar)) {
fn(myVar);
}
这个问题可能更一般地是关于创建一个类型保护,告诉编译器您的值是一个新类型,其中所述字段不是可选的,而是必需的 一种方法是使用TypeScript附带的
Required
类型,该类型将所有的字段翻转为必填字段。然而,一个更现实的场景是,可能不是所有字段都被检查存在,但只有一些字段被检查存在
下面是针对这种情况的泛型类型和typeguard的示例:
/** Interface with optional properties */
interface IOptionalData {
foo?: { bar?: string };
other?: { bar?: string};
always: { bar?: string };
}
/** Utility to make certain keys of a type required */
type RequiredKeys<T, K extends keyof T> = Exclude<T, K> & Required<Pick<T, K>>
/** Typeguard for property 'foo' in IOptionalData */
const ensureFooProperty = (data: IOptionalData): data is RequiredKeys<IOptionalData, 'foo'> =>
!!data.foo && typeof data.foo.bar === 'string'
const accessData = (data: IOptionalData) => {
if (ensureFooProperty(data)) {
console.log(data.always.bar) // always is always defined
console.log(data.other.bar) // COMPILER ERROR: 'other' is possibly undefined
return data.foo.bar // accessing optional props is allowed due to ensureToFoo
}
console.log(data.foo.bar) // COMPILER ERROR: 'foo' is possibly undefined
}
/**具有可选属性的接口*/
接口数据{
foo?:{bar?:string};
其他?:{bar?:string};
始终:{bar?:string};
}
/**用于制作所需类型的某些键的实用程序*/
类型RequiredKeys=排除和必需
/**IOPerationalData中属性“foo”的Typeguard*/
const-ensureOproperty=(数据:IOOptionalData):需要数据键=>
!!data.foo&&typeof data.foo.bar==='string'
const accessData=(数据:iooptionaldata)=>{
如果(确保财产(数据)){
console.log(data.always.bar)//始终是定义的
console.log(data.other.bar)//编译器错误:“other”可能未定义
return data.foo.bar//由于ensureToFoo,允许访问可选道具
}
console.log(data.foo.bar)//编译器错误:“foo”可能未定义
}
注意:在我的示例中,您总是可以将检查内联到if语句中,但是,由于DRY(您的类型保护可能会更复杂一些)的原因,这并不总是最佳的操作过程。旧问题,但是在较新版本的typescript中有一个非常干净的解决方案
fn(myVar!);
如果你认真对待我的问题,也许会更容易理解。例如,如果我使用外部库,我无法更改
接口秒
的定义,但它仍然与接口第一
兼容。问题是,由于可选属性,接口第二
与接口第一
不兼容。不管怎样,我介绍的3个解决方案在本例中都有效。谢谢,我知道类型保护,但它的作用有点不同,因为我不需要检查参数的类型。我有两个兼容的对象,我相信这是澄清TypeScript这一事实的有效方法。但它们不兼容<代码>{a:any}与{a?:any}
的类型不同。除了类型assert或类型guard之外,您无法为编译器澄清这一点。对于我来说,您的RequiredKeys实用程序类型没有按预期工作。这个版本做到了:type RequiredKeys=Exclude&{[key in K]-?:Required}