为何`state is NoteInitial || state is NewNote`无法识别子类属性?
这绝对不是编译器的问题,是Dart类型系统的特性导致的,咱们来拆解一下:
为什么单独判断可行,用||就报错?
当你单独写state is NoteInitial或者state is NewNote时,Dart会触发类型提升——编译器会自动把state的类型从父类NoteState提升到对应的子类(比如NoteInitial),这时候你能访问子类的notes属性完全没问题。
但当你用||把两个判断连起来时,Dart的类型推断没办法把state的类型推断成「NoteInitial 或 NewNote」的联合类型(早期Dart完全不支持联合类型,即使是Dart 3.0+引入了相关特性,这种场景下也不会自动做合并推断)。所以编译器只能把state当成父类NoteState来处理,而你的抽象类NoteState里并没有定义notes属性,自然就会报错了。
解决办法
这里有几个靠谱的方案,你可以根据自己的代码结构选:
方案1:给抽象父类添加notes抽象属性
这是最优雅的方案,既然两个子类都有notes,那直接把它提到父类里定义成抽象getter,让子类去实现:
abstract class NoteState { // 定义抽象getter,强制子类实现 List<YourNoteType> get notes; } class NoteInitial extends NoteState { @override final List<YourNoteType> notes; NoteInitial(this.notes); } class NewNote extends NoteState { @override final List<YourNoteType> notes; NewNote(this.notes); }
这样不管state是哪个子类,编译器都知道它属于NoteState,而NoteState有notes属性,你的原代码就能直接正常编译了。
方案2:拆分判断逻辑
如果不想修改父类,可以把判断拆成两个独立的分支,这样每个分支里都会触发类型提升:
if (state is NoteInitial) { return ListView.builder( itemCount: state.notes.length, padding: EdgeInsets.symmetric(vertical: 10), itemBuilder: (BuildContext context, int index) { return _NoteCard(state.notes[index], key: UniqueKey()); }); } else if (state is NewNote) { return ListView.builder( itemCount: state.notes.length, padding: EdgeInsets.symmetric(vertical: 10), itemBuilder: (BuildContext context, int index) { return _NoteCard(state.notes[index], key: UniqueKey()); }); }
缺点是会有重复代码,你可以把ListView的逻辑抽成一个单独的方法来复用。
方案3:用Dart 3.0+的联合类型(推荐如果版本允许)
如果你的项目已经升级到Dart 3.0及以上,可以用联合类型来手动指定state的类型:
if (state is NoteInitial || state is NewNote) { // 手动把state转换成联合类型 final typedState = state as NoteInitial | NewNote; return ListView.builder( itemCount: typedState.notes.length, padding: EdgeInsets.symmetric(vertical: 10), itemBuilder: (BuildContext context, int index) { return _NoteCard(typedState.notes[index], key: UniqueKey()); }); }
Dart 3.0支持的联合类型会让编译器识别到typedState具备两个子类共有的notes属性,从而正常编译。
内容的提问来源于stack exchange,提问作者Skipper HUN

