I ended up setting a 150ms delay and setting display:none to make the item disappear completely before animating in the new list. This seems to work in all my required cases.
export const fadeInOutListItemAnimation = trigger('fadeInOutListItem', [
state('void', style({ opacity: 0, display: 'none'})), // Initial state when the element is not present
transition(':enter', [
animate('150ms 150ms ease-in', style({ opacity: 1, display: '*'}))
]), // When the element enters
transition(':leave', [
animate('150ms ease-out', style({ opacity: 0, display: 'none' })),
]) // When the element leaves
]);